///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	function vxSetupServices() {
		var mod = vx.module('v');
		mod.provider({
			$animation : $AnimationProvider,
			$animator : $AnimatorProvider,
			$browser : $BrowserProvider,
			$cacheFactory : $CacheFactoryProvider,
			$compile : $CompileProvider,
			$controller : $ControllerProvider,
			$document : $DocumentProvider,
			//$exceptionHandler : $ExceptionHandlerProvider,
			$filter : $FilterProvider,
			$interpolate : $InterpolateProvider,
			$http : $HttpProvider,
			$httpBackend : $HttpBackendProvider,
			$log : $LogProvider,
			$parse : $ParseProvider,
			$rootScope : $RootScopeProvider,
			$q : $QProvider,
			$sniffer : $SnifferProvider,
			$templateCache : $TemplateCacheProvider,
			$window : $WindowProvider,
			$timeout : $TimeoutProvider,
			$targets : $TargetsProvider,
			$$remote : $$RemoteProvider,
			$validation : $ValidationProvider
		});
	}

	/**
	 * @vxdoc object
	 * @name vx.$animationProvider
	 * @description
	 *
	 * The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
	 * of a module.
	 *
	 */
	$AnimationProvider.$inject = ['$provide'];
	function $AnimationProvider($provide) {
		var suffix = 'Animation';

		/**
		 * @vxdoc function
		 * @name vx.$animation#register
		 * @methodOf vx.$animationProvider
		 *
		 * @description
		 * Registers a new injectable animation factory function. The factory function produces the animation object which
		 * has these two properties:
		 *
		 *   * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose
		 *   of this function is to get the element ready for animation. Optionally the function returns an memento which
		 *   is passed to the `start` function.
		 *   * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on
		 *   element animation completion, and an optional memento from the `setup` function.
		 *
		 * @param {string} name The name of the animation.
		 * @param {function} factory The factory function that will be executed to return the animation object.
		 *
		 */
		this.register = function(name, factory) {
			$provide.factory(camelCase(name) + suffix, factory);
		};

		this.$get = ['$injector',
		function($injector) {
			/**
			 * @vxdoc function
			 * @name vx.$animation
			 * @function
			 *
			 * @description
			 * The $animation service is used to retrieve any defined animation functions. When executed, the $animation service
			 * will return a object that contains the setup and start functions that were defined for the animation.
			 *
			 * @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored
			 *        inside of the VX DI so a call to $animate('custom') is the same as injecting `customAnimation`
			 *        via dependency injection.
			 * @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation.
			 */
			return function $animation(name) {
				if (name) {
					var animationName = camelCase(name) + suffix;
					if ($injector.has(animationName)) {
						return $injector.get(animationName);
					}
				}
			};
		}];
	}

	// NOTE: this is a pseudo directive.

	/**
	 * @vxdoc directive
	 * @name vx.directive:vAnimate
	 *
	 * @description
	 * The `vAnimate` directive works as an attribute that is attached alongside pre-existing directives.
	 * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place
	 * without burdening the directive which uses the animation with animation details. The built in directives
	 * `vRepeat`, `vInclude`, `vSwitch`, `vShow`, `vHide` and `vView` already accept `vAnimate` directive.
	 * Custom directives can take advantage of animation through {@link vx.$animator $animator service}.
	 *
	 * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng vx directives:
	 *
	 * | Directive                                                 | Supported Animations                               |
	 * |========================================================== |====================================================|
	 * | {@link vx.directive:vRepeat#animations vRepeat}         | enter, leave and move                              |
	 * | {@link vx.directive:vView#animations vView}             | enter and leave                                    |
	 * | {@link vx.directive:vInclude#animations vInclude}       | enter and leave                                    |
	 * | {@link vx.directive:vSwitch#animations vSwitch}         | enter and leave                                    |
	 * | {@link vx.directive:vIf#animations vIf}                 | enter and leave                                    |
	 * | {@link vx.directive:vShow#animations vShow & vHide}    | show and hide
	 *
	 * You can find out more information about animations upon visiting each directive page.
	 *
	 * Below is an example of a directive that makes use of the vAnimate attribute:
	 *
	 * <pre>
	 * <!-- you can also use data-v-animate, v:animate or x-v-animate as well -->
	 * <ANY v-directive v-animate="{event1: 'animation-name', event2: 'animation-name-2'}"></ANY>
	 *
	 * <!-- you can also use a short hand -->
	 * <ANY v-directive v-animate=" 'animation' "></ANY>
	 * <!-- which expands to -->
	 * <ANY v-directive v-animate="{ enter: 'animation-enter', leave: 'animation-leave', ...}"></ANY>
	 *
	 * <!-- keep in mind that v-animate can take expressions -->
	 * <ANY v-directive v-animate=" computeCurrentAnimation() "></ANY>
	 * </pre>
	 *
	 * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
	 *
	 * Keep in mind that if an animation is running, no child element of such animation can also be animated.
	 *
	 * <h2>CSS-defined Animations</h2>
	 * By default, vAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation.
	 * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as
	 * well as CSS animations.
	 * The following code below demonstrates how to perform animations using **CSS transitions** with vAnimate:
	 *
	 * <pre>
	 * <style type="text/css">
	 * /&#42;
	 *  The animate-enter CSS class is the event name that you
	 *  have provided within the vAnimate attribute.
	 * &#42;/
	 * .animate-enter {
	 *  -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
	 *  -moz-transition: 1s linear all; /&#42; Firefox &#42;/
	 *  -o-transition: 1s linear all; /&#42; Opera &#42;/
	 *  transition: 1s linear all; /&#42; IE10+ and Future Browsers &#42;/
	 *
	 *  /&#42; The animation preparation code &#42;/
	 *  opacity: 0;
	 * }
	 *
	 * /&#42;
	 *  Keep in mind that you want to combine both CSS
	 *  classes together to avoid any CSS-specificity
	 *  conflicts
	 * &#42;/
	 * .animate-enter.animate-enter-active {
	 *  /&#42; The animation code itself &#42;/
	 *  opacity: 1;
	 * }
	 * </style>
	 *
	 * <div v-directive v-animate="{enter: 'animate-enter'}"></div>
	 * </pre>
	 *
	 * The following code below demonstrates how to perform animations using **CSS animations** with vAnimate:
	 *
	 * <pre>
	 * <style type="text/css">
	 * .animate-enter {
	 *   -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
	 *   -moz-animation: enter_sequence 1s linear; /&#42; Firefox &#42;/
	 *   -o-animation: enter_sequence 1s linear; /&#42; Opera &#42;/
	 *   animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
	 * }
	 * &#64-webkit-keyframes enter_sequence {
	 *   from { opacity:0; }
	 *   to { opacity:1; }
	 * }
	 * &#64-moz-keyframes enter_sequence {
	 *   from { opacity:0; }
	 *   to { opacity:1; }
	 * }
	 * &#64-o-keyframes enter_sequence {
	 *   from { opacity:0; }
	 *   to { opacity:1; }
	 * }
	 * &#64keyframes enter_sequence {
	 *   from { opacity:0; }
	 *   to { opacity:1; }
	 * }
	 * </style>
	 *
	 * <div v-directive v-animate="{enter: 'animate-enter'}"></div>
	 * </pre>
	 *
	 * vAnimate will first examine any CSS animation code and then fallback to using CSS transitions.
	 *
	 * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then,
	 * the active class is added to trigger the animation. The vAnimate directive will automatically extract the duration
	 * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
	 * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
	 * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
	 * has no CSS transition/animation classes surrounding it.
	 *
	 * <h2>JavaScript-defined Animations</h2>
	 * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not
	 * yet support them, then you can make use of JavaScript animations defined inside of your vx module.
	 *
	 * <pre>
	 * var vModule = vx.module('YourApp', []);
	 * vModule.animation('animate-enter', function() {
	 *   return {
	 *     setup : function(element) {
	 *       //prepare the element for animation
	 *       element.css({ 'opacity': 0 });
	 *       var memo = "..."; //this value is passed to the start function
	 *       return memo;
	 *     },
	 *     start : function(element, done, memo) {
	 *       //start the animation
	 *       element.animate({
	 *         'opacity' : 1
	 *       }, function() {
	 *         //call when the animation is complete
	 *         done()
	 *       });
	 *     }
	 *   }
	 * });
	 * </pre>
	 *
	 * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
	 * can be used in the same way with the vAnimate attribute. Keep in mind that, when using JavaScript-enabled
	 * animations, vAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using
	 * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values.
	 * It will instead close off the animation once the provided done function is executed. So it's important that you
	 * make sure your animations remember to fire off the done function once the animations are complete.
	 *
	 * @param {expression} vAnimate Used to configure the DOM manipulation animations.
	 *
	 */

	var $AnimatorProvider = function() {
		var V_ANIMATE_CONTROLLER = '$vAnimateController';
		var rootAnimateController = {
			running : true
		};

		this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope',
		function($animation, $window, $sniffer, $rootElement, $rootScope) {
			$rootElement.data(V_ANIMATE_CONTROLLER, rootAnimateController);

			/**
			 * @vxdoc function
			 * @name vx.$animator
			 * @function
			 *
			 * @description
			 * The $animator service provides the DOM manipulation API which is decorated with animations.
			 *
			 * @param {Scope} scope the scope for the v-animate.
			 * @param {Attributes} attr the attributes object which contains the vAnimate key / value pair. (The attributes are
			 *        passed into the linking function of the directive using the `$animator`.)
			 * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
			 */
			var AnimatorService = function(scope, attrs) {
				var animator = {};

				/**
				 * @vxdoc function
				 * @name vx.animator#enter
				 * @methodOf vx.$animator
				 * @function
				 *
				 * @description
				 * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
				 *
				 * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
				 * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
				 * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
				 */
				animator.enter = animateActionFactory('enter', insert, noop);

				/**
				 * @vxdoc function
				 * @name vx.animator#leave
				 * @methodOf vx.$animator
				 * @function
				 *
				 * @description
				 * Runs the leave animation operation and, upon completion, removes the element from the DOM.
				 *
				 * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
				 * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
				 */
				animator.leave = animateActionFactory('leave', noop, remove);

				/**
				 * @vxdoc function
				 * @name vx.animator#move
				 * @methodOf vx.$animator
				 * @function
				 *
				 * @description
				 * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
				 * add the element directly after the after element if present. Then the move animation will be run.
				 *
				 * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
				 * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
				 * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
				 */
				animator.move = animateActionFactory('move', move, noop);

				/**
				 * @vxdoc function
				 * @name vx.animator#show
				 * @methodOf vx.$animator
				 * @function
				 *
				 * @description
				 * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
				 *
				 * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
				 */
				animator.show = animateActionFactory('show', show, noop);

				/**
				 * @vxdoc function
				 * @name vx.animator#hide
				 * @methodOf vx.$animator
				 *
				 * @description
				 * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
				 *
				 * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
				 */
				animator.hide = animateActionFactory('hide', noop, hide);

				/**
				 * @vxdoc function
				 * @name vx.animator#animate
				 * @methodOf vx.$animator
				 *
				 * @description
				 * Triggers a custom animation event to be executed on the given element
				 *
				 * @param {string} event the name of the custom event
				 * @param {jQuery/jqLite element} element the element that will be animated
				 */
				animator.animate = function(event, element) {
					animateActionFactory(event, noop, noop)(element);
				};
				return animator;

				function animateActionFactory(type, beforeFn, afterFn) {
					return function(element, parent, after, backward) {
						var vAnimateValue = scope.$eval(attrs.vAnimate);
						var className = vAnimateValue ? isObject(vAnimateValue) ? vAnimateValue[type] : vAnimateValue + '-' + type : '';

						var animationPolyfill = $animation(className);
						var polyfillSetup = animationPolyfill && animationPolyfill.setup;
						var polyfillStart = animationPolyfill && animationPolyfill.start;
						var polyfillCancel = animationPolyfill && animationPolyfill.cancel;

						if (!className) {
							beforeFn(element, parent, after, backward);
							afterFn(element, parent, after, backward);
						} else {
							var activeClassName = className + '-active';
							if (backward) {
								className += '-backward';
								activeClassName += '-backward';
							}
							if (!parent) {
								parent = after ? after.parent() : element.parent();
							}
							if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) || (parent.inheritedData(V_ANIMATE_CONTROLLER) || noop).running) {
								beforeFn(element, parent, after, backward);
								afterFn(element, parent, after, backward);
								return;
							}

							var animationData = element.data(V_ANIMATE_CONTROLLER) || {};
							if (animationData.running) {
								(polyfillCancel || noop)(element);
								animationData.done();
							}
							element.data(V_ANIMATE_CONTROLLER, {
								running : true,
								done : done
							});

							element.addClass(className);
							beforeFn(element, parent, after, backward);
							if (element.length === 0)
								return done();

							var memento = (polyfillSetup || noop)(element, backward);

							// $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
							// keep at 1 for animation dom rerender
							$window.setTimeout(beginAnimation, 1);
						}

						function parseMaxTime(str) {
							var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : [];
							forEach(values, function(value) {
								total = Math.max(parseFloat(value) || 0, total);
							});
							return total;
						}

						function beginAnimation() {
							element.addClass(activeClassName);
							if (polyfillStart) {
								polyfillStart(element, done, memento);
							} else if (isFunction($window.getComputedStyle)) {
								//one day all browsers will have these properties
								var w3cAnimationProp = 'animation';
								var w3cTransitionProp = 'transition';

								//but some still use vendor-prefixed styles
								var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
								var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';

								var durationKey = 'Duration', delayKey = 'Delay', animationIterationCountKey = 'IterationCount', duration = 0;

								//we want all the styles defined before and after
								var ELEMENT_NODE = 1;
								forEach(element, function(element) {
									if (element.nodeType == ELEMENT_NODE) {
										var elementStyles = $window.getComputedStyle(element) || {};

										var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]), parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));

										var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]), parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));

										var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]), parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));

										var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]), parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));

										if (animationDuration > 0) {
											animationDuration *= Math.max(int(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0, int(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0, 1);
										}

										duration = Math.max(animationDelay + animationDuration, transitionDelay + transitionDuration, duration);
									}
								});
								$window.setTimeout(done, duration * 1000);
							} else {
								done();
							}
						}

						function done() {
							if (!done.run) {
								done.run = true;
								afterFn(element, parent, after, backward);
								element.removeClass(className);
								element.removeClass(activeClassName);
								element.removeData(V_ANIMATE_CONTROLLER);
							}
						}

					};
				}

				function show(element) {
					element.css('display', '');
				}

				function hide(element) {
					element.css('display', 'none');
				}

				function insert(element, parent, after) {
					var afterNode = after && after[after.length - 1];
					var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
					var afterNextSibling = afterNode && afterNode.nextSibling;
					forEach(element, function(node) {
						if (afterNextSibling) {
							parentNode.insertBefore(node, afterNextSibling);
						} else {
							parentNode.appendChild(node);
						}
					});
				}

				function remove(element) {
					element.remove();
				}

				function move(element, parent, after) {
					// Do not remove element before insert. Removing will cause data associated with the
					// element to be dropped. Insert will implicitly do the remove.
					insert(element, parent, after);
				}

			};
			/**
			 * @vxdoc function
			 * @name vx.animator#enabled
			 * @methodOf vx.$animator
			 * @function
			 *
			 * @param {Boolean=} If provided then set the animation on or off.
			 * @return {Boolean} Current animation state.
			 *
			 * @description
			 * Globally enables/disables animations.
			 *
			 */
			AnimatorService.enabled = function(value) {
				if (arguments.length) {
					rootAnimateController.running = !value;
				}
				return !rootAnimateController.running;
			};

			return AnimatorService;
		}];
	};

	////////////////////////////////
	// $WindowProvider
	///////////////////////////////

	/**
	 * @vxdoc object
	 * @name vx.$window
	 *
	 * @description
	 * A reference to the browser's `window` object. While `window`
	 * is globally available in JavaScript, it causes testability problems, because
	 * it is a global variable. In vx we always refer to it through the
	 * `$window` service, so it may be overridden, removed or mocked for testing.
	 *
	 * All expressions are evaluated with respect to current scope so they don't
	 * suffer from window globality.
	 *
	 * @example
	 <doc:example>
	 <doc:source>
	 <script>
	 function Ctrl($scope, $window) {
	 $scope.$window = $window;
	 $scope.greeting = 'Hello, World!';
	 }
	 </script>
	 <div v-controller="Ctrl">
	 <input type="text" v-model="greeting" />
	 <button v-click="$window.alert(greeting)">ALERT</button>
	 </div>
	 </doc:source>
	 <doc:scenario>
	 it('should display the greeting in the input box', function() {
	 input('greeting').enter('Hello, E2E Tests');
	 // If we click the button it will block the test runner
	 // element(':button').click();
	 });
	 </doc:scenario>
	 </doc:example>
	 */
	function $WindowProvider() {
		this.$get = valueFn(window);
	}

	////////////////////////////////
	// $DocumentProvider
	///////////////////////////////

	/**
	 * @vxdoc object
	 * @name vx.$document
	 * @requires $window
	 *
	 * @description
	 * A {@link vx.element jQuery (lite)}-wrapped reference to the browser's `window.document`
	 * element.
	 */
	function $DocumentProvider() {
		this.$get = ['$window',
		function(window) {
			return jqLite(window.document);
		}];

	}

	// used by $log
	function formatError(arg) {
		if ( arg instanceof Error) {
			if (arg.stack) {
				arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ? 'Error: ' + arg.message + '\n' + arg.stack : arg.stack;
			} else if (arg.sourceURL) {
				arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
			}
		}
		return arg;
	}

	function $LogProvider() {
		var fallback, console = window.console || (window.console = {}), logger = (function() {
			var originalFunctions = [], levels = ['debug', 'info', 'warn', 'error'];
			function init(minLevel) {
				minLevel = minLevel || 'info';
				var clear = false, unsupport = function() {
					if (fallback)
						_fastInvoke(fallback, logger, arguments);
				};
				console.log = console.log || unsupport;
				for (var i = 0; i < levels.length; i++) {
					var level = levels[i];
					originalFunctions[i] = originalFunctions[i] || console[level] || (level === 'debug' ? console.log : unsupport);
					if (level && (clear || level === minLevel)) {
						console[level] = originalFunctions[i];
						clear = true;
					} else {
						console[level] = suppressed(level);
					}
				}
			}

			function reset() {
				for (var i = originalFunctions.length; i >= 0; i--) {
					console[levels[i]] = originalFunctions[i];
				}
				originalFunctions = [];
			}

			init('debug');
			return {
				levels : levels,
				init : init,
				reset : reset
			};

			function suppressed(level) {
				return function() {
					return level + ' suppressed';
				};
			}

		})();

		this.setLevel = function(level) {
			logger.init(level);
		};

		this.setFallback = function(p) {
			fallback = p;
		};

		this.$get = valueFn(console);
	}

	////////////////////////////////
	// $ExceptionHandlerProvider
	///////////////////////////////

	/**
	 * @vxdoc function
	 * @name vx.$exceptionHandler
	 * @requires $log
	 *
	 * @description
	 * Any uncaught exception in vx expressions is delegated to this service.
	 * The default implementation simply delegates to `$log.error` which logs it into
	 * the browser console.
	 *
	 * In unit tests, if `vx-mocks.js` is loaded, this service is overridden by
	 * {@link vMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
	 *
	 * @param {Error} exception Exception associated with the error.
	 * @param {string=} cause optional information about the context in which
	 *       the error was thrown.
	 */
	/*
	function $ExceptionHandlerProvider() {
	this.$get = ['$log',
	function($log) {
	return function(exception, cause) {
	// XXX call will lose line number
	//$log.error.apply($log, arguments);
	};
	}];

	}
	*/

	////////////////////////////////
	// $SnifferProvider
	///////////////////////////////

	/**
	 * !!! This is an undocumented "private" service !!!
	 *
	 * @name vx.$sniffer
	 * @requires $window
	 * @requires $document
	 *
	 * @property {boolean} history Does the browser support html5 history api ?
	 * @property {boolean} hashchange Does the browser support hashchange event ?
	 * @property {boolean} transitions Does the browser support CSS transition events ?
	 * @property {boolean} animations Does the browser support CSS animation events ?
	 *
	 * @description
	 * This is very simple implementation of testing browser's features.
	 */
	function $SnifferProvider() {
		this.$get = ['$window', '$document',
		function($window, $document) {
			var eventSupport = {}, //
			android = int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), //
			document = $document[0] || {}, //
			vendorPrefix, //
			vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, //
			bodyStyle = document.body && document.body.style, //
			transitions = false, //
			animations = false, //
			match;

			if (bodyStyle) {
				for (var prop in bodyStyle) {
					/*jshint boss:true */
					if ( match = vendorRegex.exec(prop)) {
						vendorPrefix = match[0];
						vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
						break;
					}
				}
				transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
				animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
			}
			return {
				// Android has history.pushState, but it does not update location correctly
				// so let's not use the history API at all.
				// http://code.google.com/p/android/issues/detail?id=17471
				// https://github.com/vx/vx.js/issues/904
				android : android,
				history : !!($window.history && $window.history.pushState && (!android || android >= 4)),
				hashchange : 'onhashchange' in $window &&
				// IE8 compatible mode lies
				(!document.documentMode || document.documentMode > 7),
				hasEvent : function(event) {
					// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
					// it. In particular the event is not fired when backspace or delete key are pressed or
					// when cut operation is performed.
					if (event == 'input' && msie == 9)
						return false;

					if (isUndefined(eventSupport[event])) {
						var divElm = document.createElement('div');
						eventSupport[event] = 'on' + event in divElm;
						if (msie)
							IE_GC(divElm);
					}
					return eventSupport[event];
				},
				csp : document.securityPolicy ? document.securityPolicy.isActive : false,
				vendorPrefix : vendorPrefix,
				transitions : transitions,
				animations : animations
			};
		}];

	}

	////////////////////////////////
	// $BrowserProvider
	///////////////////////////////

	function $BrowserProvider() {
		//XXX in e2etest, debounce must be disable, otherwise the state of form and inputs can not get immediately
		var e2eTest = false;
		this.setE2ETest = function(m) {
			e2eTest = !!m;
		};

		/**
		 * ! This is a private undocumented service !
		 *
		 * @name vx.$browser
		 * @requires $log
		 * @description
		 * This object has two goals:
		 *
		 * - hide all the global state in the browser caused by the window object
		 * - abstract away all the browser specific features and inconsistencies
		 *
		 * For tests we provide {@link vMock.$browser mock implementation} of the `$browser`
		 * service, which can be used for convenient testing of the application without the interaction with
		 * the real browser APIs.
		 */
		/**
		 * @param {object} window The global window object.
		 * @param {object} document jQuery wrapped document.
		 * @param {function()} XHR XMLHttpRequest constructor.
		 * @param {object} $log console.log or an object with the same interface.
		 * @param {object} $sniffer $sniffer service
		 */
		function Browser(window, document, $log, $sniffer) {
			var self = this, rawDocument = document[0], location = window.location, history = window.history, //
			setTimeout = window.setTimeout, clearTimeout = window.clearTimeout, pendingDeferIds = {};
			self.isMock = false;

			var outstandingRequestCount = 0;
			var outstandingRequestCallbacks = [];

			// TODO(vojta): remove this temporary api
			self.$$completeOutstandingRequest = completeOutstandingRequest;
			self.$$incOutstandingRequestCount = function() {
				outstandingRequestCount++;
			};
			/**
			 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
			 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
			 */
			function completeOutstandingRequest(fn) {
				try {
					_fastInvoke(fn, null, sliceArgs(arguments, 1));
				} finally {
					outstandingRequestCount--;
					if (outstandingRequestCount === 0) {
						while (outstandingRequestCallbacks.length) {
							try {
								outstandingRequestCallbacks.pop()();
							} catch (e) {
								$log.error(formatError(e));
							}
						}
					}
				}
			}

			/**
			 * @private
			 * Note: this method is used only by scenario runner
			 * @param {function()} callback Function that will be called when no outstanding request
			 */
			self.$$notifyWhenNoOutstandingRequests = function(callback) {
				// force browser to execute all pollFns - this is needed so that cookies and other pollers fire
				// at some deterministic time in respect to the test runner's actions. Leaving things up to the
				// regular poller would result in flaky tests.
				forEach(pollFns, function(pollFn) {
					pollFn();
				});
				if (outstandingRequestCount === 0) {
					callback();
				} else {
					outstandingRequestCallbacks.push(callback);
				}
			};
			//***************************
			// Poll Watcher API
			//***************************
			var pollFns = [], pollTimeout;

			/**
			 * @name vx.$browser#addPollFn
			 * @methodOf vx.$browser
			 *
			 * @param {function()} fn Poll function to add
			 *
			 * @description
			 * Adds a function to the list of functions that poller periodically executes,
			 * and starts polling if not started yet.
			 *
			 * @returns {function()} the added function
			 */
			self.addPollFn = function(fn) {
				if (isUndefined(pollTimeout))
					startPoller(100, setTimeout);
				pollFns.push(fn);
				return fn;
			};
			/**
			 * @param {number} interval How often should browser call poll functions (ms)
			 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
			 *
			 * @description
			 * Configures the poller to run in the specified intervals, using the specified
			 * setTimeout fn and kicks it off.
			 */
			function startPoller(interval, setTimeout) {
				(function check() {
					forEach(pollFns, function(pollFn) {
						pollFn();
					});
					pollTimeout = setTimeout(check, interval);
				})();
			}

			//**********************
			// URL API
			//**********************
			var lastBrowserUrl = location.href, baseElement = document.find('base');

			/**
			 * @name vx.$browser#url
			 * @methodOf vx.$browser
			 *
			 * @description
			 * GETTER:
			 * Without any argument, this method just returns current value of location.href.
			 *
			 * SETTER:
			 * With at least one argument, this method sets url to new value.
			 * If html5 history api supported, pushState/replaceState is used, otherwise
			 * location.href/location.replace is used.
			 * Returns its own instance to allow chaining
			 *
			 * NOTE: this api is intended for use only by the $location service. Please use the
			 * {@link vx.$location $location service} to change url.
			 *
			 * @param {string} url New url (when used as setter)
			 * @param {boolean=} replace Should new url replace current history record ?
			 */
			self.url = function(url, replace) {
				if (url) {// setter
					if (lastBrowserUrl == url)
						return;
					lastBrowserUrl = url;
					if ($sniffer.history) {
						if (replace)
							history.replaceState(null, '', url);
						else {
							history.pushState(null, '', url);
							// Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
							baseElement.attr('href', baseElement.attr('href'));
						}
					} else {
						$$url(url, replace);
					}
					return self;
				} else {// getter
					return $$url();
				}
			};

			var $$url = function urlGetterSetter(url, replace) {
				if (url) {
					if (replace)
						location.replace(url);
					else
						location.href = url;
				} else {
					// the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
					return location.href.replace(/%27/g, "'");
				}
			};

			self.hash = function(hash) {
				if (hash) {
					location.hash = hash;
					return self;
				} else {
					return location.hash;
				}
			};

			self.useHistory = function() {
				if (msie < 8) {
					var ieHandler = {
						iframe : jqLite('<iframe id="vx_IE_sudo_history_Frame" style="display:none" src="javascript:false;"></iframe>').prependTo('body')[0],
						getLoc : function(update) {
							var doc = this.iframe.contentWindow.document;
							if (update) {
								doc.open();
								doc.close();
							}
							return doc.location;
						},
						url : function(url, replace) {
							if (!url)
								return this.getLoc().href;
							if (replace)
								this.getLoc(true).replace(url);
							else
								this.getLoc(true).href = url;
						},
						hash : function(hash) {
							if (!hash)
								return this.getLoc().hash;
							this.getLoc(true).hash = hash;
						}
					};
					ieHandler.url(location.href);
					$$url = function urlGetterSetter(url, replace) {
						if (url) {
							ieHandler.url(url, replace);
						} else {
							return ieHandler.url();
						}
					};
					self.hash = function(hash) {
						if (hash) {
							ieHandler.hash(hash);
							return self;
						} else {
							return ieHandler.hash();
						}
					};
				}

				if (!$sniffer.history && $sniffer.android) {
					var sudoLink = jqLite('<a href="#" id="vx_android_sudo_history" style="display:none">Hash_Forward</a>').prependTo('body');
				}

				self.History = {
					add : function(state, url) {
						if ($sniffer.history) {
							history.pushState(state, null, url);
							fireUrlChange();
						} else {
							//XXX android native browser 2.3-4.1 has no history.pushState,
							//       and cannot refresh address bar synchronized
							//       so emulate 'click' trigger history tracking
							if (sudoLink) {
								sudoLink.attr('href', url);
								//XXX webkit cannot click() except input and button, so use dispatch
								try {
									var event = document.createEvent("Event");
									event.initEvent('click', true, true);
									sudoLink[0].dispatchEvent(event);
								} catch (d) {
									$log.error('cannot dispatch "click" on sudoLink');
								}
							} else {
								self.hash(url);
							}
						}
					},
					go : function(steps) {
						history.go(steps);
					},
					reload : function(steps) {
						$log.debug('reload base url "' + self.baseUrl + '"');
						if (steps) {
							//XXX use location.replace(...) keep browser history tracking
							if (!$sniffer.history && !$sniffer.android/*webkit*/) {
								// in ie/firefox, will clear history directly, but webkit is so different
								history.go(steps);
								update();
							} else {
								// webkit, history.go is async, so defer location.replace(...)
								history.go(steps);
								self.defer(function() {
									update();
								}, 0);
							}
						} else {
							update();
						}

						function update() {
							location.replace(self.baseUrl);
						}

					}
				};
			};

			var urlChangeListeners = [], urlChangeInit = false;
			function fireUrlChange() {
				if (lastBrowserUrl == self.url())
					return;
				lastBrowserUrl = self.url();
				forEach(urlChangeListeners, function(listener) {
					listener(self.url());
				});
			}

			/**
			 * @name vx.$browser#onUrlChange
			 * @methodOf vx.$browser
			 * @TODO(vojta): refactor to use node's syntax for events
			 *
			 * @description
			 * Register callback function that will be called, when url changes.
			 *
			 * It's only called when the url is changed by outside of vx:
			 * - user types different url into address bar
			 * - user clicks on history (forward/back) button
			 * - user clicks on a link
			 *
			 * It's not called when url is changed by $browser.url() method
			 *
			 * The listener gets called with new url as parameter.
			 *
			 * NOTE: this api is intended for use only by the $location service. Please use the
			 * {@link vx.$location $location service} to monitor url changes in vx apps.
			 *
			 * @param {function(string)} listener Listener function to be called when url changes.
			 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
			 */
			self.onUrlChange = function(callback) {
				if (!urlChangeInit) {
					// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
					// don't fire popstate when user change the address bar and don't fire hashchange when url
					// changed by push/replaceState

					// html5 history api - popstate event
					if ($sniffer.history)
						jqLite(window).bind('popstate', fireUrlChange);
					// hashchange event
					if ($sniffer.hashchange)
						jqLite(window).bind('hashchange', fireUrlChange);

					if (!$sniffer.history && !$sniffer.hashchange)
						self.addPollFn(fireUrlChange);
					urlChangeInit = true;
				}

				urlChangeListeners.push(callback);
				return callback;
			};

			//////////////////////////////////////////////////////////////
			// Misc API
			//////////////////////////////////////////////////////////////
			self.baseUrl = (function baseUrl() {
				var href = baseElement.attr('href') || self.url();
				if (href.indexOf('#') !== -1)
					href = href.substring(0, href.indexOf('#'));
				return href;
			})();

			/**
			 * Returns current <base href>
			 * (always relative - without domain)
			 *
			 * @returns {string=}
			 */
			self.baseHref = function() {
				return self.baseUrl.replace(/^https?\:\/\/[^\/]*/, '');
				//var href = baseElement.attr('href');
				//return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
			};
			//****************************
			// Cookies API
			//****************************
			var lastCookies = {};
			var lastCookieString = '';
			var cookiePath = self.baseHref();

			/**
			 * @name vx.$browser#cookies
			 * @methodOf vx.$browser
			 *
			 * @param {string=} name Cookie name
			 * @param {string=} value Cookie value
			 *
			 * @description
			 * The cookies method provides a 'private' low level access to browser cookies.
			 * It is not meant to be used directly, use the $cookie service instead.
			 *
			 * The return values vary depending on the arguments that the method was called with as follows:
			 * <ul>
			 *   <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li>
			 *   <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li>
			 *   <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li>
			 * </ul>
			 *
			 * @returns {Object} Hash of all cookies (if called without any parameter)
			 */
			self.cookies = function(name, value) {
				var cookieLength, cookieArray, cookie, i, index;

				if (name) {
					if (value === undefined) {
						rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
					} else {
						if (isString(value)) {
							cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;

							// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
							// - 300 cookies
							// - 20 cookies per unique domain
							// - 4096 bytes per cookie
							if (cookieLength > 4096) {
								$log.warn("Cookie '" + name + "' possibly not set or overflowed because it was too large (" + cookieLength + " > 4096 bytes)!");
							}
						}
					}
				} else {
					if (rawDocument.cookie !== lastCookieString) {
						lastCookieString = rawDocument.cookie;
						cookieArray = lastCookieString.split("; ");
						lastCookies = {};
						for ( i = 0; i < cookieArray.length; i++) {
							cookie = cookieArray[i];
							index = cookie.indexOf('=');
							if (index > 0) {//ignore nameless cookies
								var n = unescape(cookie.substring(0, index));
								// the first value that is seen for a cookie is the most
								// specific one.  values for the same cookie name that
								// follow are for less specific paths.
								if (lastCookies[n] === undefined) {
									lastCookies[n] = unescape(cookie.substring(index + 1));
								}
							}
						}
					}
					return lastCookies;
				}
			};
			/**
			 * @name vx.$browser#defer
			 * @methodOf vx.$browser
			 * @param {function()} fn A function, who's execution should be defered.
			 * @param {number=} [delay=0] of milliseconds to defer the function execution.
			 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
			 *
			 * @description
			 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
			 *
			 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
			 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
			 * via `$browser.defer.flush()`.
			 *
			 */
			self.defer = function(fn, delay) {
				var timeoutId;
				outstandingRequestCount++;
				timeoutId = setTimeout(function() {
					delete pendingDeferIds[timeoutId];
					completeOutstandingRequest(fn);
				}, delay || 0);
				pendingDeferIds[timeoutId] = true;
				return timeoutId;
			};
			/**
			 * @name vx.$browser#defer.cancel
			 * @methodOf vx.$browser.defer
			 *
			 * @description
			 * Cancels a defered task identified with `deferId`.
			 *
			 * @param {*} deferId Token returned by the `$browser.defer` function.
			 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled.
			 */
			self.defer.cancel = function(deferId) {
				if (pendingDeferIds[deferId]) {
					delete pendingDeferIds[deferId];
					clearTimeout(deferId);
					completeOutstandingRequest(noop);
					return true;
				}
				return false;
			};

			/**
			 * Creates a function that will delay the execution of `func` until after
			 * `wait` milliseconds have elapsed since the last time it was invoked. Pass
			 * `true` for `immediate` to cause debounce to invoke `func` on the leading,
			 * instead of the trailing, edge of the `wait` timeout. Subsequent calls to
			 * the debounced function will return the result of the last `func` call.
			 *
			 * @static
			 * @memberOf _
			 * @category Functions
			 * @param {Function} func The function to debounce.
			 * @param {Number} wait The number of milliseconds to delay.
			 * @param {Boolean} immediate A flag to indicate execution is on the leading
			 *  edge of the timeout.
			 * @returns {Function} Returns the new debounced function.
			 * @example
			 *
			 * var lazyLayout = _.debounce(calculateLayout, 300);
			 * jQuery(window).on('resize', lazyLayout);
			 */
			// this special version of debounce support immediate action deferred by delay action
			//****** for input/event and submit, submit must defer ensure input model correct
			var delayFns = [], deferFns = [];
			self.debounce = function(func, wait, immediate, defer) {
				if (e2eTest)
					return func;

				var timeoutId, result;

				function exec(thisArg, args) {
					var isImmediate = immediate && (defer || !timeoutId);
					clearTimeout(timeoutId);
					timeoutId = setTimeout(delayed, wait);
					if (isImmediate) {
						result = func.apply(thisArg, args);
					}
					return result;
					function delayed() {
						timeoutId = null;
						if (!immediate) {
							result = func.apply(thisArg, args);
						} else if ( typeof immediate == 'function') {
							immediate();
						}
						if (!defer) {
							arrayRemove(delayFns, exec);
							if (delayFns.length === 0) {
								for (var i = 0; i < deferFns.length; i++)
									deferFns[i]();
								deferFns = [];
							}
						}
					}

				}

				return function() {
					var thisArg = this, args = arguments;
					if (!defer) {
						if (indexOf(delayFns, exec) === -1)
							delayFns.push(exec);
					} else if (delayFns.length > 0) {
						deferFns.push(function() {
							return exec(thisArg, args);
						});
						return;
					}
					return exec(thisArg, args);
				};
			};
		}

		// original version debounce
		/*
		 self.debounce = function(func, wait, immediate) {
		 if (e2eTest)
		 return func;

		 var args, result, thisArg, timeoutId;

		 function delayed() {
		 timeoutId = null;
		 if (!immediate) {
		 result = func.apply(thisArg, args);
		 } else if ( typeof immediate == 'function') {
		 immediate();
		 }
		 }

		 return function() {
		 var isImmediate = immediate && !timeoutId;
		 args = arguments;
		 thisArg = this;

		 clearTimeout(timeoutId);
		 timeoutId = setTimeout(delayed, wait);

		 if (isImmediate) {
		 result = func.apply(thisArg, args);
		 }
		 return result;
		 };
		 };
		 }
		 */

		this.$get = ['$window', '$log', '$sniffer', '$document',
		function($window, $log, $sniffer, $document) {
			return new Browser($window, $document, $log, $sniffer);
		}];

	}

	////////////////////////////////
	// $CacheFactoryProvider
	///////////////////////////////

	/**
	 * @vxdoc object
	 * @name vx.$cacheFactory
	 *
	 * @description
	 * Factory that constructs cache objects.
	 *
	 *
	 * @param {string} cacheId Name or id of the newly created cache.
	 * @param {object=} options Options object that specifies the cache behavior. Properties:
	 *
	 *   - `{number=}` `capacity` — turns the cache into LRU cache.
	 *
	 * @returns {object} Newly created cache object with the following set of methods:
	 *
	 * - `{object}` `info()` — Returns id, size, and options of cache.
	 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and return it.
	 * - `{{*}}' `get({string} key) — Returns cached value for `key` or undefined for cache miss.
	 * - `{void}` `remove({string} key)' — Removes a key-value pair from the cache.
	 * - `{void}` `removeAll()' — Removes all cached values.
	 * - `{void}` `destroy()' — Removes references to this cache from $cacheFactory.
	 *
	 */
	function $CacheFactoryProvider() {
		this.$get = function() {
			var caches = {};
			function cacheFactory(cacheId, options) {
				if ( cacheId in caches) {
					throw vError(30, "CacheId '{0}' is already taken!", cacheId);
				}
				var size = 0, stats = extend({}, options, {
					id : cacheId
				}), data = {}, capacity = (options && options.capacity) || Number.MAX_VALUE, lruHash = {}, freshEnd = null, staleEnd = null;
				return caches[cacheId] = {
					key : function(key) {
						return key;
					},
					put : function(key, value) {
						key = this.key(key);
						var lruEntry = lruHash[key] || (lruHash[key] = {
							key : key
						});
						refresh(lruEntry);
						if (isUndefined(value))
							return;
						if (!( key in data))
							size++;
						data[key] = value;
						if (size > capacity) {
							this.remove(staleEnd.key);
						}
						return value;
					},
					get : function(key) {
						key = this.key(key);
						var lruEntry = lruHash[key];
						if (!lruEntry)
							return;
						refresh(lruEntry);
						return data[key];
					},
					remove : function(key) {
						key = this.key(key);
						var lruEntry = lruHash[key];
						if (!lruEntry)
							return;
						if (lruEntry == freshEnd)
							freshEnd = lruEntry.p;
						if (lruEntry == staleEnd)
							staleEnd = lruEntry.n;
						link(lruEntry.n, lruEntry.p);
						delete lruHash[key];
						delete data[key];
						size--;
					},
					removeAll : function() {
						data = {};
						size = 0;
						lruHash = {};
						freshEnd = staleEnd = null;
					},
					destroy : function() {
						data = null;
						stats = null;
						lruHash = null;
						delete caches[cacheId];
					},
					info : function() {
						return extend({}, stats, {
							size : size
						});
					}
				};

				/**
				 * makes the 'entry' the freshEnd of the LRU linked list
				 */
				function refresh(entry) {
					if (entry != freshEnd) {
						if (!staleEnd) {
							staleEnd = entry;
						} else if (staleEnd == entry) {
							staleEnd = entry.n;
						}
						link(entry.n, entry.p);
						link(entry, freshEnd);
						freshEnd = entry;
						freshEnd.n = null;
					}
				}

				/**
				 * bidirectionally links two entries of the LRU linked list
				 */
				function link(nextEntry, prevEntry) {
					if (nextEntry != prevEntry) {
						if (nextEntry)
							nextEntry.p = prevEntry;
						//p stands for previous, 'prev' didn't minify
						if (prevEntry)
							prevEntry.n = nextEntry;
						//n stands for next, 'next' didn't minify
					}
				}

			}


			cacheFactory.info = function() {
				var info = {};
				forEach(caches, function(cache, cacheId) {
					info[cacheId] = cache.info();
				});
				return info;
			};

			cacheFactory.get = function(cacheId) {
				return caches[cacheId];
			};
			return cacheFactory;
		};
	}

	////////////////////////////////
	// $TemplateCacheProvider
	///////////////////////////////
	/**
	 * @vxdoc object
	 * @name vx.$templateCache
	 *
	 * @description
	 * Cache used for storing html templates.
	 *
	 * See {@link vx.$cacheFactory $cacheFactory}.
	 *
	 */
	function $TemplateCacheProvider() {
		this.$get = ['$cacheFactory',
		function($cacheFactory) {
			var c = $cacheFactory('templates');
			var rts = /([?&])[_v]=[^&]*(&)?/;
			c.key = function(key) {
				if (isString(key)) {
					key = key.replace(rts, "$1");
				}
				return key;
			};
			return c;
		}];

	}

	////////////////////////////////
	// $TargetsProvider
	///////////////////////////////
	function $TargetsProvider() {
		var targets = {}, transitions = {}, trackLine = {
			count : 0,
			history : -1,
			index : 0,
			tracks : []
		}, useLocation = false;

		var DEFAULT_TRANSITION = function(oldEl, newEl, remove, back) {
			if (newEl && newEl.length) {
				if (oldEl && oldEl.length) {
					oldEl.css('display', 'none');
					if (remove)
						oldEl.remove();
				}
				newEl.css('display', '');
			}
		};

		this.useLocation = function(yes) {
			useLocation = !!yes;
		};

		function transition(name, fn) {
			var old = transitions[name];
			if (!fn)
				return old || DEFAULT_TRANSITION;
			transitions[name] = fn;
			return old;
		}


		this.transition = transition;

		Factory.$inject = ['$window', '$browser', '$log', '$rootScope'];
		function Factory(window, $browser, $log, $rootScope) {
			var viewports = {};

			if (useLocation)
				$browser.useHistory();

			forward.clearTarget = function(target) {
				viewports[target] = undefined;
				targets[target] = undefined;
			};

			/*
			 *  targets service, could be:
			 *  1. page fn(target, pageUrl[, active])
			 *  2. viewport
			 *     fn(target, url[, noHistory])
			 */
			function forward(target, urlExp, noHistory) {
				var view;
				if (isObject(target) && target.$trackPage) {// is ViewportController
					view = target;
				} else if (isString(target)) {
					view = viewports[target];
					if (!view)
						throw vError(31, 'no viewport found: {0}', target);
				} else {
					throw vError(32, 'target must be viewportController or its id');
				}
				target = view.$id;
				var old = targets[target], url = urlExp;
				if (!url)
					return old;

				if (url.indexOf('#') === 0) {
					var page = url.substring(1);
					view.$trackPage(page, true);
					return;
				}

				if (old && url === old.url)
					return;

				if (noHistory === undefined)
					noHistory = view.$noHistory;

				var current = {
					target : target,
					url : url
				};

				trackLine.count++;

				$log.debug('targets forward to "' + url + '" in "' + target + '" ' + (!noHistory ? '[with track]' : '[without track]'));

				if (!noHistory) {
					addHistory(current);
				}

				toViewport(target, current);
			}

			function addHistory(current) {
				if (trackLine.index >= 0 && trackLine.index <= trackLine.tracks.length) {
					trackLine.tracks.splice(trackLine.index, trackLine.tracks.length - trackLine.index, current);
				} else {
					throw vError(33, 'invalid history index: {0}', trackLine.index);
				}
				trackLine.index++;
				if (useLocation) {
					var loc = "#/$" + trackLine.index;
					$browser.History.add({
						index : trackLine.index
					}, loc);
					trackLine.history = window.history.length;
				}
			}

			function toViewport(target, track, back) {
				targets[target] = track;
				viewports[target].$load(track, back);
			}

			function trackHistory(index) {
				var back = index < trackLine.index;
				trackLine.index = index;
				var cur = trackLine.tracks[index - 1];
				$log.debug('targets track to "' + cur.url + '" in "' + cur.target + '"');
				toViewport(cur.target, cur, back);
			}

			if (useLocation) {
				$browser.onUrlChange(function(url) {
					var hash = $browser.hash();
					if (!hash || hash.indexOf('#/$') !== 0)
						return;
					var index = int(hash.substring(3));
					if (!isNumber(index) || index < 1)
						throw vError(34, 'invalid history index: {0}', index);
					if (index > trackLine.tracks.length)
						return;
					if (trackLine.index !== index) {
						trackHistory(index);
					}
				});
			}

			forward.track = function(steps) {
				var flag = steps.match(/^([\+\-]?)(\d+)$/);
				if (!flag)
					return;
				var n = int(steps);
				if (flag[1]) {// relative
					if (trackLine.tracks.length < 2)
						return;
					if (n <= -trackLine.index)
						n = -trackLine.index + 1;
					if (n > trackLine.tracks.length - trackLine.index)
						n = trackLine.tracks.length - trackLine.index;
					if (n === 0)
						return;
					if (useLocation) {
						$browser.History.go(n);
					} else {
						trackHistory(trackLine.index + n);
					}
				} else {
					if (n === 0) {
						//XXX n === 0 is special, it means reset history
						// history.count prevent reload dead loop
						if (trackLine.count > 1)
							return $browser.History.reload(-trackLine.index);
						return;
					}

					if (n > trackLine.tracks.length)
						throw vError(35, 'history over max index: {0}', trackLine.tracks.length);
					if (n === trackLine.index)
						return;
					if (useLocation) {
						$browser.History.go(n - trackLine.index);
					} else {
						trackHistory(n);
					}
				}
			};
			forward.track.info = trackLine;
			forward.transition = transition;

			forward.$viewports = viewports;

			return forward;
		}

		// return forward Factory
		this.$get = Factory;
	}

	//*****************************
	// $FilterProvider
	//*****************************

	/**
	 * @vxdoc object
	 * @name vx.$filterProvider
	 * @description
	 *
	 * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
	 * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
	 * responsible for creating a filter function.
	 *
	 * <pre>
	 *   // Filter registration
	 *   function MyModule($provide, $filterProvider) {
	 *     // create a service to demonstrate injection (not always needed)
	 *     $provide.value('greet', function(name){
	 *       return 'Hello ' + name + '!';
	 *     });
	 *
	 *     // register a filter factory which uses the
	 *     // greet service to demonstrate DI.
	 *     $filterProvider.register('greet', function(greet){
	 *       // return the filter function which uses the greet service
	 *       // to generate salutation
	 *       return function(text) {
	 *         // filters need to be forgiving so check input validity
	 *         return text && greet(text) || text;
	 *       };
	 *     });
	 *   }
	 * </pre>
	 *
	 * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`.
	 * <pre>
	 *   it('should be the same instance', inject(
	 *     function($filterProvider) {
	 *       $filterProvider.register('reverse', function(){
	 *         return ...;
	 *       });
	 *     },
	 *     function($filter, reverseFilter) {
	 *       expect($filter('reverse')).toBe(reverseFilter);
	 *     });
	 * </pre>
	 *
	 *
	 * For more information about how vx filters work, and how to create your own filters, see
	 * {@link guide/dev_guide.templates.filters Understanding VX Filters} in the vx Developer
	 * Guide.
	 */
	/**
	 * @vxdoc method
	 * @name vx.$filterProvider#register
	 * @methodOf vx.$filterProvider
	 * @description
	 * Register filter factory function.
	 *
	 * @param {String} name Name of the filter.
	 * @param {function} fn The filter factory function which is injectable.
	 */

	/**
	 * @vxdoc function
	 * @name vx.$filter
	 * @function
	 * @description
	 * Filters are used for formatting data displayed to the user.
	 *
	 * The general syntax in templates is as follows:
	 *
	 *         {{ expression [| filter_name[:parameter_value] ... ] }}
	 *
	 * @param {String} name Name of the filter function to retrieve
	 * @return {Function} the filter function
	 */
	$FilterProvider.$inject = ['$provide'];
	function $FilterProvider($provide) {
		var suffix = 'Filter', register = this.register = function(name, factory) {
			return $provide.factory(name + suffix, factory);
		};

		this.$get = ['$injector',
		function($injector) {
			return function(name) {
				return $injector.get(name + suffix);
			};
		}];

	}

	////////////////////////////////
	// $ParseProvider
	///////////////////////////////
	/**
	 * @vxdoc function
	 * @name vx.$parse
	 * @function
	 *
	 * @description
	 *
	 * Converts VX {@link guide/expression expression} into a function.
	 *
	 * <pre>
	 *   var getter = $parse('user.name');
	 *   var setter = getter.assign;
	 *   var context = {user:{name:'vx'}};
	 *   var locals = {user:{name:'local'}};
	 *
	 *   expect(getter(context)).toEqual('vx');
	 *   setter(context, 'newValue');
	 *   expect(context.user.name).toEqual('newValue');
	 *   expect(getter(context, locals)).toEqual('local');
	 * </pre>
	 *
	 *
	 * @param {string} expression String expression to compile.
	 * @returns {function(context, locals)} a function which represents the compiled expression:
	 *
	 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
	 *      are evaluated against (typically a scope object).
	 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
	 *      `context`.
	 *
	 *    The returned function also has the following properties:
	 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
	 *        literal.
	 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
	 *        constant literals.
	 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
	 *        set to a function to change its value on the given context.
	 *
	 */
	function $ParseProvider() {
		var cache = {};
		this.$get = ['$filter', '$sniffer',
		function($filter, $sniffer) {
			return function(exp) {
				switch(typeof exp) {
					case 'string':
						return cache.hasOwnProperty(exp) ? cache[exp] : cache[exp] = EXPR.parser(exp, false, $filter, $sniffer.csp);
					case 'function':
						return exp;
					default:
						return noop;
				}
			};
		}];

	}

	////////////////////////////////
	// $InterpolateProvider
	///////////////////////////////

	/**
	 * @vxdoc object
	 * @name vx.$interpolateProvider
	 * @function
	 *
	 * @description
	 *
	 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
	 */
	function $InterpolateProvider() {
		var startSymbol = '{{';
		var endSymbol = '}}';

		/**
		 * @vxdoc method
		 * @name vx.$interpolateProvider#startSymbol
		 * @methodOf vx.$interpolateProvider
		 * @description
		 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
		 *
		 * @param {string=} value new value to set the starting symbol to.
		 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
		 */
		this.startSymbol = function(value) {
			if (value) {
				startSymbol = value;
				return this;
			} else {
				return startSymbol;
			}
		};
		/**
		 * @vxdoc method
		 * @name vx.$interpolateProvider#endSymbol
		 * @methodOf vx.$interpolateProvider
		 * @description
		 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
		 *
		 * @param {string=} value new value to set the ending symbol to.
		 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
		 */
		this.endSymbol = function(value) {
			if (value) {
				endSymbol = value;
				return this;
			} else {
				return endSymbol;
			}
		};

		this.$get = ['$parse', /*'$exceptionHandler',*/'$log',
		function($parse, /*$exceptionHandler,*/$log) {
			var startSymbolLength = startSymbol.length, endSymbolLength = endSymbol.length;

			/**
			 * @vxdoc function
			 * @name vx.$interpolate
			 * @function
			 *
			 * @requires $parse
			 *
			 * @description
			 *
			 * Compiles a string with markup into an interpolation function. This service is used by the
			 * HTML {@link vx.$compile $compile} service for data binding. See
			 * {@link vx.$interpolateProvider $interpolateProvider} for configuring the
			 * interpolation markup.
			 *
			 *
			 <pre>
			 var $interpolate = ...; // injected
			 var exp = $interpolate('Hello {{name}}!');
			 expect(exp({name:'VX'}).toEqual('Hello VX!');
			 </pre>
			 *
			 *
			 * @param {string} text The text with markup to interpolate.
			 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
			 *    embedded expression in order to return an interpolation function. Strings with no
			 *    embedded expression will return null for the interpolation function.
			 * @returns {function(context)} an interpolation function which is used to compute the interpolated
			 *    string. The function has these parameters:
			 *
			 *    * `context`: an object against which any expressions embedded in the strings are evaluated
			 *      against.
			 *
			 */
			function $interpolate(text, mustHaveExpression) {
				var startIndex, endIndex, index = 0, parts = [], length = text.length, hasInterpolation = false, fn, exp, concat = [], isStatic = true;

				while (index < length) {
					if ((( startIndex = text.indexOf(startSymbol, index)) != -1) && (( endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
						if (index != startIndex)
							parts.push(text.substring(index, startIndex));
						parts.push( fn = parseExp(exp = text.substring(startIndex + startSymbolLength, endIndex)));
						fn.exp = exp;
						index = endIndex + endSymbolLength;
						if(!fn.$static)
							isStatic = false;
						hasInterpolation = true;
					} else {
						// we did not find anything, so we have to add the remainder to the parts array
						if (index != length)
							parts.push(text.substring(index));
						index = length;
					}
				}

				if (!( length = parts.length)) {
					// we added, nothing, must have been an empty string.
					parts.push('');
					length = 1;
				}

				if (!mustHaveExpression || hasInterpolation) {
					concat.length = length;
					fn = function(context, locals) {
						try {
							for (var i = 0, ii = length, part; i < ii; i++) {
								if ( typeof ( part = parts[i]) == 'function') {
									part = part(context, locals);
									if (part === null || part === undefined) {
										part = '';
									} else if ( typeof part != 'string') {
										part = toJson(part);
									}
								}
								concat[i] = part;
							}
							return concat.join('');
						} catch(err) {
							var newErr = vError(48, "$interpolate error! Can't interpolate: {0}\n{1}", text, err.toString());
							$log.error('Error while interpolating: ' + text + '\n', formatError(newErr));
							//$exceptionHandler(newErr);
						}
					};
					fn.$static = isStatic;
					fn.exp = text;
					fn.parts = parts;
					return fn;
				}
			}
			
			function parseExp(exp) {
				var isStatic = false;
				if(exp.charAt(0) === '#'){
					isStatic = true;
					if(exp.charAt(1) === '#'){
						exp = '$msg(' + exp.substring(2) + ')';
					} else {
						exp = '$field(' + exp.substring(1) + ')';
					}
				}
				var fn = $parse(exp);
				fn.$static = isStatic;
				return fn;
			}

			/**
			 * @vxdoc method
			 * @name v.$interpolate#startSymbol
			 * @methodOf v.$interpolate
			 * @description
			 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
			 *
			 * Use {@link v.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
			 * the symbol.
			 *
			 * @returns {string} start symbol.
			 */
			$interpolate.startSymbol = function() {
				return startSymbol;
			};
			/**
			 * @vxdoc method
			 * @name v.$interpolate#endSymbol
			 * @methodOf v.$interpolate
			 * @description
			 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
			 *
			 * Use {@link v.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
			 * the symbol.
			 *
			 * @returns {string} start symbol.
			 */
			$interpolate.endSymbol = function() {
				return endSymbol;
			};

			return $interpolate;
		}];

	}

	////////////////////////////////
	// $ControllerProvider
	///////////////////////////////
	/**
	 * @vxdoc object
	 * @name vx.$controllerProvider
	 * @description
	 * The {@link vx.$controller $controller service} is used by VX to create new
	 * controllers.
	 *
	 * This provider allows controller registration via the
	 * {@link vx.$controllerProvider#register register} method.
	 */
	function $ControllerProvider() {
		var controllers = {}, CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;

		/**
		 * @vxdoc function
		 * @name vx.$controllerProvider#register
		 * @methodOf vx.$controllerProvider
		 * @param {string} name Controller name
		 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
		 *    annotations in the array notation).
		 */
		this.register = function(name, constructor) {
			if (isObject(name)) {
				extend(controllers, name);
			} else {
				controllers[name] = constructor;
			}
		};

		this.$get = ['$injector', '$window',
		function($injector, $window) {
			/**
			 * @vxdoc function
			 * @name vx.$controller
			 * @requires $injector
			 *
			 * @param {Function|string} constructor If called with a function then it's considered to be the
			 *    controller constructor function. Otherwise it's considered to be a string which is used
			 *    to retrieve the controller constructor using the following steps:
			 *
			 *    * check if a controller with given name is registered via `$controllerProvider`
			 *    * check if evaluating the string on the current scope returns a constructor
			 *    * check `window[constructor]` on the global `window` object
			 *
			 * @param {Object} locals Injection locals for Controller.
			 * @return {Object} Instance of given controller.
			 *
			 * @description
			 * `$controller` service is responsible for instantiating controllers.
			 *
			 * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
			 * a service, so that one can override this service with {@link https://gist.github.com/1649788
			 * BC version}.
			 */
			return function(expression, locals) {
				var instance, match, constructor, identifier;

				if (isString(expression)) {
					match = expression.match(CNTRL_REG), constructor = match[1], identifier = match[3];
					expression = controllers.hasOwnProperty(constructor) ? controllers[constructor] : //
					getter(locals.$scope.$config, constructor, true) || getter(locals.$scope, constructor, true) || getter($window, constructor, true);
					assertArgFn(expression, constructor, true);
				}

				instance = $injector.instantiate(expression, locals);

				if (identifier) {
					if (!(locals && typeof locals.$scope == 'object')) {
						throw vError(36, "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", constructor || expression.name, identifier);
					}
					locals.$scope[identifier] = instance;
				}

				return instance;
			};
		}];

	}

	////////////////////////////////
	// $CompileProvider
	///////////////////////////////
	/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
	*
	* DOM-related variables:
	*
	* - "node" - DOM Node
	* - "element" - DOM Element or Node
	* - "$node" or "$element" - jqLite-wrapped node or element
	*
	*
	* Compiler related stuff:
	*
	* - "linkFn" - linking fn of a single directive
	* - "nodeLinkFn" - function that aggregates all linking fns for a particular node
	* - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
	* - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
	*/

	var BOOLEAN_ATTR = {};
	forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
		BOOLEAN_ATTR[lowercase(value)] = value;
	});
	var BOOLEAN_ELEMENTS = {};
	forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
		BOOLEAN_ELEMENTS[uppercase(value)] = true;
	});
	function getBooleanAttrName(element, name) {
		// check dom last since we will most likely fail on name
		var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];

		// booleanAttr is here twice to minimize DOM access
		return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
	}

	/**
	 * Converts all accepted directives format into proper directive name.
	 * All of these will become 'myDirective':
	 *   my:DiRective
	 *   my-directive
	 *   x-my-directive
	 *   data-my:directive
	 *
	 * Also there is special case for Moz prefix starting with upper case letter.
	 * @param name Name to normalize
	 */
	var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
	function directiveNormalize(name) {
		return camelCase(name.replace(PREFIX_REGEXP, ''));
	}

	/**
	 * @vxdoc function
	 * @name vx.$compile
	 * @function
	 *
	 * @description
	 * Compiles a piece of HTML string or DOM into a template and produces a template function, which
	 * can then be used to link {@link vx.$rootScope.Scope scope} and the template together.
	 *
	 * The compilation is a process of walking the DOM tree and trying to match DOM elements to
	 * {@link vx.$compileProvider#directive directives}. For each match it
	 * executes corresponding template function and collects the
	 * instance functions into a single template function which is then returned.
	 *
	 * The template function can then be used once to produce the view or as it is the case with
	 * {@link vx.directive:vRepeat repeater} many-times, in which
	 * case each call results in a view that is a DOM clone of the original template.
	 *
	 <doc:example module="compile">
	 <doc:source>
	 <script>
	 // declare a new module, and inject the $compileProvider
	 vx.module('compile', [], function($compileProvider) {
	 // configure new 'compile' directive by passing a directive
	 // factory function. The factory function injects the '$compile'
	 $compileProvider.directive('compile', function($compile) {
	 // directive factory creates a link function
	 return function(scope, element, attrs) {
	 scope.$watch(
	 function(scope) {
	 // watch the 'compile' expression for changes
	 return scope.$eval(attrs.compile);
	 },
	 function(value) {
	 // when the 'compile' expression changes
	 // assign it into the current DOM
	 element.html(value);

	 // compile the new DOM and link it to the current
	 // scope.
	 // NOTE: we only compile .childNodes so that
	 // we don't get into infinite loop compiling ourselves
	 $compile(element.contents())(scope);
	 }
	 );
	 };
	 })
	 });

	 function Ctrl($scope) {
	 $scope.name = 'VX';
	 $scope.html = 'Hello {{name}}';
	 }
	 </script>
	 <div v-controller="Ctrl">
	 <input v-model="name"> <br>
	 <textarea v-model="html"></textarea> <br>
	 <div compile="html"></div>
	 </div>
	 </doc:source>
	 <doc:scenario>
	 it('should auto compile', function() {
	 expect(element('div[compile]').text()).toBe('Hello VX');
	 input('html').enter('{{name}}!');
	 expect(element('div[compile]').text()).toBe('VX!');
	 });
	 </doc:scenario>
	 </doc:example>

	 *
	 *
	 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
	 * @param {function(vx.Scope[, cloneAttachFn]} transclude function available to directives.
	 * @param {number} maxPriority only apply directives lower then given priority (Only effects the
	 *                 root element(s), not their children)
	 * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template
	 * (a DOM element/tree) to a scope. Where:
	 *
	 *  * `scope` - A {@link vx.$rootScope.Scope Scope} to bind to.
	 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
	 *               `template` and call the `cloneAttachFn` function allowing the caller to attach the
	 *               cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
	 *               called as: <br> `cloneAttachFn(clonedElement, scope)` where:
	 *
	 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
	 *      * `scope` - is the current scope with which the linking function is working with.
	 *
	 * Calling the linking function returns the element of the template. It is either the original element
	 * passed in, or the clone of the element if the `cloneAttachFn` is provided.
	 *
	 * After linking the view is not updated until after a call to $digest which typically is done by
	 * VX automatically.
	 *
	 * If you need access to the bound view, there are two ways to do it:
	 *
	 * - If you are not asking the linking function to clone the template, create the DOM element(s)
	 *   before you send them to the compiler and keep this reference around.
	 *   <pre>
	 *     var element = $compile('<p>{{total}}</p>')(scope);
	 *   </pre>
	 *
	 * - if on the other hand, you need the element to be cloned, the view reference from the original
	 *   example would not point to the clone, but rather to the original template that was cloned. In
	 *   this case, you can access the clone via the cloneAttachFn:
	 *   <pre>
	 *     var templateHTML = vx.element('<p>{{total}}</p>'),
	 *         scope = ....;
	 *
	 *     var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
	 *       //attach the clone to DOM document at the right place
	 *     });
	 *
	 *     //now we have reference to the cloned DOM via `clone`
	 *   </pre>
	 *
	 *
	 * For information on how the compiler works, see the
	 * {@link guide/compiler VX HTML Compiler} section of the Developer Guide.
	 */
	$CompileProvider.$inject = ['$provide'];
	function $CompileProvider($provide) {
		var hasDirectives = {}, Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, //
		CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, //
		urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;

		/**
		 * @vxdoc function
		 * @name vx.$compileProvider#directive
		 * @methodOf vx.$compileProvider
		 * @function
		 *
		 * @description
		 * Register a new directives with the compiler.
		 *
		 * @param {string} name Name of the directive in camel-case. (ie <code>vBind</code> which will match as
		 *                <code>v-bind</code>).
		 * @param {function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more
		 *                info.
		 * @returns {vx.$compileProvider} Self for chaining.
		 */
		this.directive = supportObject(registerDirective);

		function registerDirective(name, directiveFactory) {
			/*
			 TODO: directive FORMAT
			 directive {
			 priority:
			 name:
			 require:
			 restrict:
			 replace:
			 scope:
			 controller:
			 transclude:   //'element'
			 template:
			 templateUrl:
			 terminal:
			 compile:
			 link:
			 }
			 */
			assertArg(directiveFactory, 'directive');
			var replace;
			if (name.indexOf('*') === 0) {
				replace = true;
				name = name.substring(1);
			}
			if (replace || !hasDirectives.hasOwnProperty(name)) {
				hasDirectives[name] = [];
				$provide.factory(name + Suffix, ['$injector', /*'$exceptionHandler',*/'$log',
				function($injector, /*$exceptionHandler,*/$log) {
					var directives = [];
					forEach(hasDirectives[name], function(directiveFactory) {
						try {
							var directive = $injector.invoke(directiveFactory);
							if (isFunction(directive)) {
								directive = {
									compile : valueFn(directive)
								};
							} else if (!directive.compile && directive.link) {
								directive.compile = valueFn(directive.link);
							}
							directive.priority = directive.priority || 0;
							directive.name = directive.name || name;
							directive.require = directive.require || (directive.controller && directive.name);
							directive.restrict = directive.restrict || 'A';
							//'EACM'
							directives.push(directive);
						} catch (e) {
							$log.error(formatError(e));
							//$exceptionHandler(e);
						}
					});
					return directives;
				}]);

			}
			hasDirectives[name].push(directiveFactory);

			/*jshint validthis:true*/
			return this;
		}

		/**
		 * @vxdoc function
		 * @name v.$compileProvider#urlSanitizationWhitelist
		 * @methodOf v.$compileProvider
		 * @function
		 *
		 * @description
		 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
		 * urls during a[href] sanitization.
		 *
		 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
		 *
		 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
		 * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
		 * expression. If a match is found the original url is written into the dom. Otherwise the
		 * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
		 *
		 * @param {RegExp=} regexp New regexp to whitelist urls with.
		 * @returns {RegExp|v.$compileProvider} Current RegExp if called without value or self for
		 *    chaining otherwise.
		 */
		this.urlSanitizationWhitelist = function(regexp) {
			if (isDefined(regexp)) {
				urlSanitizationWhitelist = regexp;
				return this;
			}
			return urlSanitizationWhitelist;
		};

		this.$get = ['$injector', '$interpolate', /*$exceptionHandler',*/'$log', '$http', '$templateCache', '$parse', '$controller', '$rootScope', '$document', compiler];

		function compiler($injector, $interpolate, /*$exceptionHandler,*/$log, $http, $templateCache, $parse, $controller, $rootScope, $document) {
			/**
			 * @vxdoc object
			 * @name vx.$compile.directive.Attributes
			 * @description
			 *
			 * A shared object between directive compile / linking functions which contains normalized DOM element
			 * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed
			 * since all of these are treated as equivalent in VX:
			 *
			 *          <span v:bind="a" v-bind="a" data-v-bind="a" x-v-bind="a">
			 */
			/**
			 * @vxdoc property
			 * @name vx.$compile.directive.Attributes#$attr
			 * @propertyOf vx.$compile.directive.Attributes
			 * @returns {object} A map of DOM element attribute names to the normalized name. This is
			 *          needed to do reverse lookup from normalized name back to actual name.
			 */
			/**
			 * @vxdoc function
			 * @name vx.$compile.directive.Attributes#$set
			 * @methodOf vx.$compile.directive.Attributes
			 * @function
			 *
			 * @description
			 * Set DOM element attribute value.
			 *
			 *
			 * @param {string} name Normalized element attribute name of the property to modify. The name is
			 *          revers translated using the {@link vx.$compile.directive.Attributes#$attr $attr}
			 *          property to the original name.
			 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
			 */
			var Attributes = function(element, attr) {
				this.$$element = element;
				this.$attr = attr || {};
			};

			Attributes.prototype = {
				$normalize : directiveNormalize,
				/**
				 * Set a normalized attribute on the element in a way such that all directives
				 * can share the attribute. This function properly handles boolean attributes.
				 * @param {string} key Normalized key. (ie vAttribute)
				 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
				 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
				 *     Defaults to true.
				 * @param {string=} attrName Optional none normalized name. Defaults to key.
				 */
				$set : function(key, value, writeAttr, attrName) {
					var booleanKey = getBooleanAttrName(this.$$element[0], key), $$observers = this.$$observers, normalizedVal;

					if (booleanKey) {
						this.$$element.prop(key, value);
						attrName = booleanKey;
					}

					this[key] = value;

					// translate normalized key to actual key
					if (attrName) {
						this.$attr[key] = attrName;
					} else {
						attrName = this.$attr[key];
						if (!attrName) {
							this.$attr[key] = attrName = snake_case(key, '-');
						}
					}

					// sanitize a[href] values
					if (_nodeName(this.$$element[0]) === 'A' && key === 'href') {
						urlSanitizationNode.setAttribute('href', value);

						// href property always returns normalized absolute url, so we can match against that
						normalizedVal = urlSanitizationNode.href;
						if (!normalizedVal.match(urlSanitizationWhitelist)) {
							this[key] = value = 'unsafe:' + normalizedVal;
						}
					}

					if (writeAttr !== false) {
						if (value === null || value === undefined) {
							this.$$element.removeAttr(attrName);
						} else {
							this.$$element.attr(attrName, value);
						}
					}

					// fire observers
					if ($$observers)
						forEach($$observers[key], function(fn) {
							try {
								fn(value);
							} catch (e) {
								$log.error(formatError(e));
								//$exceptionHandler(e);
							}
						});
				},
				/**
				 * Observe an interpolated attribute.
				 * The observer will never be called, if given attribute is not interpolated.
				 *
				 * @param {string} key Normalized key. (ie vAttribute) .
				 * @param {function(*)} fn Function that will be called whenever the attribute value changes.
				 * @returns {function(*)} the `fn` Function passed in.
				 */
				$observe : function(key, fn) {
					var attrs = this, $$observers = (attrs.$$observers || (attrs.$$observers = {})), listeners = ($$observers[key] || ($$observers[key] = []));
					//XXX observe fn $watch in addAttrInterpolateDirective
					listeners.push(fn);
					/*$rootScope*/
					attrs.$$element.scope().$evalAsync(function() {
						if (!listeners.$$inter) {
							// no one registered attribute interpolation function, so lets call it manually
							fn(attrs[key]);
						}
					});
					return fn;
				}
			};

			var urlSanitizationNode = $document[0].createElement('a'), startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), //
			denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') ? identity : function denormalizeTemplate(template) {
				return template.replace(/\{\{/g, startSymbol).replace(/\}\}/g, endSymbol);
			}, V_ATTR_BINDING = /^vAttr[A-Z]/;

			return compile;

			//================================

			function compile($compileNodes, transcludeFn, maxPriority) {
				if (!( $compileNodes instanceof jqLite)) {
					// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
					$compileNodes = jqLite($compileNodes);
				}
				var tempParent = document.createDocumentFragment();
				// We can not compile top level text elements since text nodes can be merged and we will
				// not be able to attach scope data to them, so we will wrap them in <span>
				forEach($compileNodes, function(node, index) {
					if (node.nodeType == 3/* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
						$compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0];
					}
				});
				var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority);
				return publicLinkFn;
				function publicLinkFn(scope, cloneConnectFn, scopeBinding) {
					assertArg(scope, 'scope');
					// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
					// and sometimes changes the structure of the DOM.
					var $linkNode = cloneConnectFn ? jqLiteClone($compileNodes)// IMPORTANT!!!
					: $compileNodes;

					if (scopeBinding !== false) {
						// Attach scope only to non-text nodes.
						for (var i = 0, ii = $linkNode.length; i < ii; i++) {
							var node = $linkNode[i];
							if (node.nodeType == 1/* element */ || node.nodeType == 9 /* document */) {
								jqLite(node).data('$scope', scope);
							}
						}
						safeAddClass($linkNode, 'v-scope');
					}
					if (cloneConnectFn)
						cloneConnectFn($linkNode, scope);
					if (compositeLinkFn)
						compositeLinkFn(scope, $linkNode, $linkNode);

					return $linkNode;
				}

			}

			function safeAddClass($element, className) {
				try {
					$element.addClass(className);
				} catch(e) {
					// ignore, since it means that we are trying to set class on
					// SVG element, where class name is read-only.
				}
			}

			/**
			 * Compile function matches each node in nodeList against the directives. Once all directives
			 * for a particular node are collected their compile functions are executed. The compile
			 * functions return values - the linking functions - are combined into a composite linking
			 * function, which is the a linking function for the node.
			 *
			 * @param {NodeList} nodeList an array of nodes or NodeList to compile
			 * @param {function(vx.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
			 *        scope argument is auto-generated to the new child of the transcluded parent scope.
			 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
			 *        rootElement must be set the jqLite collection of the compile root. This is
			 *        needed so that the jqLite collection items can be replaced with widgets.
			 * @param {number=} max directive priority
			 * @returns {?function} A composite linking function of all of the matched directives or null.
			 */
			function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
				var linkFns = [], nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;

				for (var i = 0; i < nodeList.length; i++) {
					attrs = new Attributes();
					// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
					directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined);
					nodeLinkFn = (directives.length) ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement) : null;
					childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)//
					? null//
					: compileNodes(nodeList[i].childNodes, nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);

					linkFns.push(nodeLinkFn);
					linkFns.push(childLinkFn);
					linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
				}

				// return a linking function if we have found anything, null otherwise
				return linkFnFound ? compositeLinkFn : null;

				function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
					var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
					var fn1 = function(transcludeFn) {
						return function(cloneFn) {
							var transcludeScope = scope.$new();
							transcludeScope.$$transcluded = true;
							return transcludeFn(transcludeScope, cloneFn).bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
						};
					};
					// copy nodeList so that linking doesn't break due to live list updates.
					var stableNodeList = [];
					for ( i = 0, ii = nodeList.length; i < ii; i++) {
						stableNodeList.push(nodeList[i]);
					}
					for ( i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
						node = stableNodeList[n];
						nodeLinkFn = linkFns[i++];
						childLinkFn = linkFns[i++];
						if (nodeLinkFn) {
							if (nodeLinkFn.scope) {
								//new scope in Controller, others child scope create by include/view/switch/repeat
								// isObject $new isolate
								childScope = scope.$new(isObject(nodeLinkFn.scope));
								jqLite(node).data('$scope', childScope);
							} else {
								childScope = scope;
							}
							childTranscludeFn = nodeLinkFn.transclude;
							if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
								nodeLinkFn(childLinkFn, childScope, node, $rootElement, fn1(childTranscludeFn || transcludeFn));
							} else {
								nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
							}
						} else if (childLinkFn) {
							childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
						}
					}
				}

			}

			/**
			 * Looks for directives on the given node and adds them to the directive collection which is sorted.
			 *
			 * @param node Node to search
			 * @param directives An array to which the directives are added to. This array is sorted before
			 *        the function returns.
			 * @param attrs The shared attrs object which is used to populate the normalized attributes.
			 * @param {number=} maxPriority Max directive priority.
			 */
			function collectDirectives(node, directives, attrs, maxPriority) {
				var nodeType = node.nodeType, attrsMap = attrs.$attr, match, className, nodeName = _nodeName(node);

				switch(nodeType) {
					case 1:
						/* Element */
						// use the node name: <directive>
						addDirective(directives, directiveNormalize(_nodeName(node).toLowerCase()), 'E', maxPriority);

						// iterate over the attributes
						for (var attr, name, nName, vAttrName, value, nAttrs = node.attributes, j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
							var attrStartName;
							var attrEndName;
							var index;

							attr = nAttrs[j];
							name = attr.name;

							//XXX in IE, other attributes not specified in node.attributes
							// jimmy**** in IE 6/7 input's value is unspecified
							if (!attr.specified) {
								if (!msie || msie > 7)
									continue;
								if (name === 'type') {
									// for IE6/7 unsupported html5 input type, eg: url/number/email and etc
									value = node.getAttribute('type');
									if (nodeName === 'BUTTON') {
										// IE 6/7 button default type is Button, it is not standard!!!
										value = 'submit';
									}
								} else if (name === 'value')
									value = trim(attr.value);
								else
									continue;
							} else {
								value = trim((msie && name === 'href') ? decodeURIComponent(node.getAttribute(name, 2)) : attr.value);
							}

							// support vAttr attribute binding
							vAttrName = directiveNormalize(name);
							if (V_ATTR_BINDING.test(vAttrName)) {
								name = vAttrName.substr(5).toLowerCase();
							}
							if (( index = vAttrName.lastIndexOf('Start')) != -1 && index == vAttrName.length - 5) {
								attrStartName = name;
								attrEndName = name.substr(0, name.length - 5) + 'end';
								name = name.substr(0, name.length - 6);
							}
							nName = directiveNormalize(name.toLowerCase());
							attrsMap[nName] = name;
							attrs[nName] = value;
							if (getBooleanAttrName(node, nName)) {
								// presence means true
								attrs[nName] = value = true;
							}
							if (value)
								addAttrInterpolateDirective(node, directives, value, nName);
							addDirective(directives, nName, 'A', maxPriority, attrStartName, attrEndName);
						}

						// use class as directive
						className = node.className;
						if (isString(className) && className !== '') {
							/*jshint boss:true*/
							while ( match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
								nName = directiveNormalize(match[2]);
								if (addDirective(directives, nName, 'C', maxPriority)) {
									attrs[nName] = trim(match[3]);
								}
								className = className.substr(match.index + match[0].length);
							}
						}
						break;
					case 3:
						/* Text Node */
						addTextInterpolateDirective(directives, node.nodeValue);
						break;
					case 8:
						/* Comment */
						try {
							match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
							if (match) {
								nName = directiveNormalize(match[1]);
								if (addDirective(directives, nName, 'M', maxPriority)) {
									attrs[nName] = trim(match[2]);
								}
							}
						} catch (e) {
							// turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value.
							// Just ignore it and continue. (Can't seem to reproduce in test case.)
						}
						break;
				}

				directives.sort(byPriority);
				return directives;

				/**
				 * Sorting function for bound directives.
				 */
				function byPriority(a, b) {
					return b.priority - a.priority;
				}

				/**
				 * looks up the directive and decorates it with exception handling and proper parameters. We
				 * call this the boundDirective.
				 *
				 * @param {string} name name of the directive to look up.
				 * @param {string} location The directive must be found in specific format.
				 *   String containing any of theses characters:
				 *
				 *   * `E`: element name
				 *   * `A': attribute
				 *   * `C`: class
				 *   * `M`: comment
				 * @returns true if directive was added.
				 */
				function addDirective(tDirectives, name, location, maxPriority, startAttrName, endAttrName) {
					var match = null;
					if (hasDirectives.hasOwnProperty(name)) {
						for (var directive, directives = $injector.get(name + Suffix), i = 0, ii = directives.length; i < ii; i++) {
							try {
								directive = directives[i];
								if ((maxPriority === undefined || maxPriority > directive.priority) && directive.restrict.indexOf(location) != -1) {
									if (startAttrName) {
										directive = inherit(directive, {
											$$start : startAttrName,
											$$end : endAttrName
										});
									}
									tDirectives.push(directive);
									match = directive;
								}
							} catch(e) {
								$log.error(formatError(e));
								//$exceptionHandler(e);
							}
						}
					}
					return match;
				}

				function addTextInterpolateDirective(directives, text) {
					var interpolateFn = $interpolate(text, true);
					if (interpolateFn) {
						directives.push({
							priority : 0,
							compile : valueFn(function textInterpolateLinkFn(scope, node) {
								if(interpolateFn.$static) {
									node[0].nodeValue = interpolateFn(scope);
									return;
								}
								var parent = node.parent(), bindings = parent.data('$binding') || [];
								bindings.push(interpolateFn);
								parent.data('$binding', bindings);
								safeAddClass(parent, 'v-binding');
								scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
									node[0].nodeValue = value;
								});
							})
						});
					}
				}

				function addAttrInterpolateDirective(node, directives, value, name) {
					var interpolateFn = $interpolate(value, true);
					// no interpolation found -> ignore
					if (!interpolateFn)
						return;
					directives.push({
						priority : 100,
						compile : valueFn(function attrInterpolateLinkFn(scope, element, attr) {
							if(interpolateFn.$static) {
								attr.$set(name, interpolateFn(scope));
								return;
							}
							
							var $$observers = (attr.$$observers || (attr.$$observers = {}));
							// we need to interpolate again, in case the attribute value has been updated
							// (e.g. by another directive's compile function)
							interpolateFn = $interpolate(attr[name], true);
							// if attribute was updated so that there is no interpolation going on we don't want to
							// register any observers
							if (!interpolateFn)
								return;

							attr[name] = interpolateFn(scope);
							($$observers[name] || ($$observers[name] = [])).$$inter = true;
							(attr.$$observers && attr.$$observers[name].$$scope || scope).$watch(interpolateFn, function interpolateFnWatchAction(value) {
								attr.$set(name, value);
							});
						})
					});
				}

			}

			/**
			 * Given a node with an directive-start it collects all of the siblings until it find directive-end.
			 * @param node
			 * @param attrStart
			 * @param attrEnd
			 * @returns {*}
			 */
			function groupScan(node, attrStart, attrEnd) {
				var nodes = [];
				var depth = 0;
				if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
					var startNode = node;
					do {
						if (!node) {
							throw vError(37, "Unterminated attribute, found '{0}' but no matching '{1}' found.", attrStart, attrEnd);
						}
						if (node.hasAttribute(attrStart))
							depth++;
						if (node.hasAttribute(attrEnd))
							depth--;
						nodes.push(node);
						node = node.nextSibling;
					} while (depth > 0);
				} else {
					nodes.push(node);
				}
				return jqLite(nodes);
			}

			/**
			 * Wrapper for linking function which converts normal linking function into a grouped
			 * linking function.
			 * @param linkFn
			 * @param attrStart
			 * @param attrEnd
			 * @returns {Function}
			 */
			function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
				return function(scope, element, attrs, controllers) {
					element = groupScan(element[0], attrStart, attrEnd);
					return linkFn(scope, element, attrs, controllers);
				};
			}

			/**
			 * Once the directives have been collected their compile functions are executed. This method
			 * is responsible for inlining directive templates as well as terminating the application
			 * of the directives if the terminal directive has been reached.
			 *
			 * @param {Array} directives Array of collected directives to execute their compile function.
			 *        this needs to be pre-sorted by priority order.
			 * @param {Node} compileNode The raw DOM node to apply the compile functions to
			 * @param {Object} templateAttrs The shared attribute function
			 * @param {function(vx.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
			 *        scope argument is auto-generated to the new child of the transcluded parent scope.
			 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
			 *        argument has the root jqLite array so that we can replace nodes on it.
			 * @returns linkFn
			 */
			function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
				var terminalPriority = -Number.MAX_VALUE, preLinkFns = [], postLinkFns = [], newScopeDirective = null, //
				newIsolateScopeDirective = null, templateDirective = null, //
				$compileNode = templateAttrs.$$element = jqLite(compileNode), // add $$element to attr object
				directive, directiveName, $template, transcludeDirective, childTranscludeFn = transcludeFn, controllerDirectives, linkFn, directiveValue;

				// executes all directives on the current element
				for (var i = 0, ii = directives.length; i < ii; i++) {
					directive = directives[i];
					var attrStart = directive.$$start;
					var attrEnd = directive.$$end;

					// collect multiblock sections
					if (attrStart) {
						$compileNode = groupScan(compileNode, attrStart, attrEnd);
					}
					$template = undefined;

					if (terminalPriority > directive.priority) {
						break;
						// prevent further processing of directives
					}

					/*jshint boss:true*/
					if ( directiveValue = directive.scope) {
						assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode);
						if (isObject(directiveValue)) {
							safeAddClass($compileNode, 'v-isolate-scope');
							newIsolateScopeDirective = directive;
						}
						safeAddClass($compileNode, 'v-scope');
						newScopeDirective = newScopeDirective || directive;
					}
					directiveName = directive.name;

					if ( directiveValue = directive.controller) {
						controllerDirectives = controllerDirectives || {};
						assertNoDuplicate("'" + directiveName + "' controller", controllerDirectives[directiveName], directive, $compileNode);
						controllerDirectives[directiveName] = directive;
					}

					if ( directiveValue = directive.transclude) {
						assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
						transcludeDirective = directive;
						terminalPriority = directive.priority;
						if (directiveValue == 'element') {
							$template = groupScan(compileNode, attrStart, attrEnd);
							$compileNode = templateAttrs.$$element = jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
							compileNode = $compileNode[0];
							replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode);
							childTranscludeFn = compile($template, transcludeFn, terminalPriority);
						} else {
							//XXX need shivForIE for unknown element below IE8
							$template = jqLite(jqLiteClone(compileNode)).contents();
							$compileNode.html('');
							// clear contents
							childTranscludeFn = compile($template, transcludeFn);
						}
					}

					if (directive.template) {
						assertNoDuplicate('template', templateDirective, directive, $compileNode);
						templateDirective = directive;
						directiveValue = (isFunction(directive.template)) ? directive.template($compileNode, templateAttrs) : directive.template;

						directiveValue = denormalizeTemplate(directiveValue);

						if (directive.replace) {
							$template = jqLite('<div>' + trim(directiveValue) + '</div>').contents();
							compileNode = $template[0];

							if ($template.length != 1 || compileNode.nodeType !== 1) {
								throw vError(38, "Template for directive '{0}' must have exactly one root element.", directiveName);
							}
							replaceWith(jqCollection, $compileNode, compileNode);
							var newTemplateAttrs = {
								$attr : {}
							};

							// combine directives from the original node and from the template:
							// - take the array of directives for this element
							// - split it into two parts, those that were already applied and those that weren't
							// - collect directives from the template, add them to the second group and sort them
							// - append the second group with new directives to the first group
							directives = directives.concat(collectDirectives(compileNode, directives.splice(i + 1, directives.length - (i + 1)), newTemplateAttrs));
							mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
							ii = directives.length;
						} else {
							$compileNode.html(directiveValue);
						}
					}

					if (directive.templateUrl) {
						assertNoDuplicate('template', templateDirective, directive, $compileNode);
						templateDirective = directive;
						//replace nodeLinkFn
						nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace, childTranscludeFn);
						ii = directives.length;
					} else if (directive.compile) {
						try {
							linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
							if (isFunction(linkFn)) {
								addLinkFns(null, linkFn, attrStart, attrEnd);
							} else if (linkFn) {
								addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
							}
						} catch (e) {
							$log.error(formatError(e), startingTag($compileNode));
							//$exceptionHandler(e, startingTag($compileNode));
						}
					}

					if (directive.terminal) {
						nodeLinkFn.terminal = true;
						terminalPriority = Math.max(terminalPriority, directive.priority);
					}

				}
				nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope;
				nodeLinkFn.transclude = transcludeDirective && childTranscludeFn;

				// IE leak?
				compileNode = $compileNode = $template = jqCollection = null;

				// might be normal or delayed nodeLinkFn depending on if templateUrl is present
				return nodeLinkFn;

				function assertNoDuplicate(what, previousDirective, directive, element) {
					if (previousDirective) {
						throw vError(39, 'Multiple directives [{0}, {1}] asking for {2} on: {3}', previousDirective.name, directive.name, what, startingTag(element));
					}
				}

				//-----------------------------------------------------

				function addLinkFns(pre, post, attrStart, attrEnd) {
					if (pre) {
						if (attrStart)
							pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
						pre.require = directive.require;
						preLinkFns.push(pre);
					}
					if (post) {
						if (attrStart)
							post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
						post.require = directive.require;
						postLinkFns.push(post);
					}
				}

				function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
					var attrs, $element, i, ii, linkFn, controller;

					if (compileNode === linkNode) {
						attrs = templateAttrs;
					} else {
						attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
					}
					$element = attrs.$$element;

					if (newIsolateScopeDirective) {
						var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;

						var parentScope = scope.$parent || scope;

						forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
							var match = definition.match(LOCAL_REGEXP) || [], attrName = match[3] || scopeName, optional = (match[2] == '?'), mode = match[1], // @, =, or &
							lastValue, parentGet, parentSet;

							scope.$$isolateBindings[scopeName] = mode + attrName;
							switch (mode) {
								case '@':
									// simple set scope
									attrs.$observe(attrName, function(value) {
										scope[scopeName] = value;
									});
									attrs.$$observers[attrName].$$scope = parentScope;
									if (attrs[attrName]) {
										// If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
										scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
									}
									break;
								case '=':
									if (optional && !attrs[attrName]) {
										return;
									}
									// sync with parent
									parentGet = $parse(attrs[attrName]);
									parentSet = parentGet.assign ||
									function() {
										// reset the change, or we will throw this exception on every $digest
										lastValue = scope[scopeName] = parentGet(parentScope);
										throw vError(40, "Expression '{0}' used with directive '{1}' is non-assignable!", attrs[attrName], newIsolateScopeDirective.name);
									};

									lastValue = scope[scopeName] = parentGet(parentScope);
									scope.$watch(function parentValueWatch() {
										var parentValue = parentGet(parentScope);
										if (parentValue !== scope[scopeName]) {
											// we are out of sync and need to copy
											if (parentValue !== lastValue) {
												// parent changed and it has precedence
												lastValue = scope[scopeName] = parentValue;
											} else {
												// if the parent can be assigned then do so
												parentSet(parentScope, parentValue = lastValue = scope[scopeName]);
											}
										}
										return parentValue;
									});
									break;
								case '&':
									// get from parent
									parentGet = $parse(attrs[attrName]);
									scope[scopeName] = function(locals) {
										return parentGet(parentScope, locals);
									};
									break;
								default:
									throw vError(41, "Invalid isolate scope definition for directive '{0}'. Definition: {... {1}: '{2}' ...}", newIsolateScopeDirective.name, scopeName, definition);
							}
						});
					}

					if (controllerDirectives) {
						forEach(controllerDirectives, function(directive) {
							var locals = {
								$scope : scope,
								$element : $element,
								$attrs : attrs,
								$transclude : boundTranscludeFn
							};
							controller = directive.controller;
							if (controller == '@') {
								controller = attrs[directive.name];
							} else if (controller == '@@') {
								controller = attrs[directive.name] || $element.controller('vViewport').$id + '_setup';
							}

							$element.data('$' + directive.name + 'Controller', $controller(controller, locals));
						});
					}

					// PRELINKING
					for ( i = 0, ii = preLinkFns.length; i < ii; i++) {
						try {
							linkFn = preLinkFns[i];
							linkFn(scope, $element, attrs, linkFn.require && getControllers(linkFn.require, $element));
						} catch (e) {
							$log.error(formatError(e), startingTag($element));
							//$exceptionHandler(e, startingTag($element));
						}
					}

					// RECURSION
					if (childLinkFn)
						childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn);

					// POSTLINKING
					for ( i = 0, ii = postLinkFns.length; i < ii; i++) {
						try {
							linkFn = postLinkFns[i];
							linkFn(scope, $element, attrs, linkFn.require && getControllers(linkFn.require, $element));
						} catch (e) {
							$log.error(formatError(e), startingTag($element));
							//$exceptionHandler(e, startingTag($element));
						}
					}
				}

				function getControllers(require, $element) {
					var value, retrievalMethod = 'data', optional = false;
					if (isString(require)) {
						while (( value = require.charAt(0)) == '^' || value == '?') {
							require = require.substr(1);
							if (value == '^') {
								retrievalMethod = 'inheritedData';
							}
							optional = optional || value == '?';
						}
						value = $element[retrievalMethod]('$' + require + 'Controller');
						if (!value && !optional) {
							throw vError(42, "Controller '{0}', required by directive '{1}', can't be found!", require, directiveName);
						}
						return value;
					} else if (isArray(require)) {
						value = [];
						forEach(require, function(require) {
							value.push(getControllers(require, $element));
						});
					}
					return value;
				}

				function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, $rootElement, replace, childTranscludeFn) {
					var linkQueue = [], afterTemplateNodeLinkFn, afterTemplateChildLinkFn, beforeTemplateCompileNode = $compileNode[0], origAsyncDirective = directives.shift(),
					// The fact that we have to copy and patch the directive seems wrong!
					derivedSyncDirective = extend({}, origAsyncDirective, {
						controller : null,
						templateUrl : null,
						transclude : null,
						scope : null
					}), templateUrl = (isFunction(origAsyncDirective.templateUrl)) ? origAsyncDirective.templateUrl($compileNode, tAttrs) : origAsyncDirective.templateUrl;

					$compileNode.html('');

					//XXX need $scope and ajaxPort???
					$http.get(templateUrl, {
						cache : $templateCache
					}).success(function(content) {
						var compileNode, tempTemplateAttrs, $template;
						content = denormalizeTemplate(content);

						if (replace) {
							$template = jqLite('<div>' + trim(content) + '</div>').contents();
							compileNode = $template[0];

							if ($template.length != 1 || compileNode.nodeType !== 1) {
								throw vError(43, "Template for directive '{0}' must have exactly one root element. Template: {1}", origAsyncDirective.name, templateUrl);
							}
							tempTemplateAttrs = {
								$attr : {}
							};
							replaceWith($rootElement, $compileNode, compileNode);
							collectDirectives(compileNode, directives, tempTemplateAttrs);
							mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
						} else {
							compileNode = beforeTemplateCompileNode;
							$compileNode.html(content);
						}

						directives.unshift(derivedSyncDirective);
						afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
						afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);

						while (linkQueue.length) {
							var scope = linkQueue.shift(), beforeTemplateLinkNode = linkQueue.shift(), linkRootElement = linkQueue.shift(), controller = linkQueue.shift(), linkNode = compileNode;

							if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
								// it was cloned therefore we have to clone as well.
								linkNode = jqLiteClone(compileNode);
								replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
							}

							/*jshint loopfunc:true*/
							afterTemplateNodeLinkFn(function() {
								beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller);
							}, scope, linkNode, $rootElement, controller);
						}
						linkQueue = null;
					}).error(function(response, code, headers, config) {
						throw vError(44, 'Failed to load template: {0}', config.url);
					});
					return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) {
						if (linkQueue) {
							linkQueue.push(scope);
							linkQueue.push(node);
							linkQueue.push(rootElement);
							linkQueue.push(controller);
						} else {
							afterTemplateNodeLinkFn(function() {
								beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller);
							}, scope, node, rootElement, controller);
						}
					};
				}

				/**
				 * When the element is replaced with HTML template then the new attributes
				 * on the template need to be merged with the existing attributes in the DOM.
				 * The desired effect is to have both of the attributes present.
				 *
				 * @param {object} dst destination attributes (original DOM)
				 * @param {object} src source attributes (from the directive template)
				 */
				function mergeTemplateAttributes(dst, src) {
					var srcAttr = src.$attr, dstAttr = dst.$attr, $element = dst.$$element;
					// reapply the old attributes to the new element
					forEach(dst, function(value, key) {
						if (key.charAt(0) != '$') {
							if (src[key]) {
								value += (key === 'style' ? ';' : ' ') + src[key];
							}
							dst.$set(key, value, true, srcAttr[key]);
						}
					});
					// copy the new attributes on the old attrs object
					forEach(src, function(value, key) {
						if (key == 'class') {
							safeAddClass($element, value);
							dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
						} else if (key == 'style') {
							$element.attr('style', $element.attr('style') + ';' + value);
						} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
							dst[key] = value;
							dstAttr[key] = srcAttr[key];
						}
					});
				}

				/**
				 * This is a special jqLite.replaceWith, which can replace items which
				 * have no parents, provided that the containing jqLite collection is provided.
				 *
				 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
				 *    in the root of the tree.
				 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep the shell,
				 *    but replace its DOM node reference.
				 * @param {Node} newNode The new DOM node.
				 */
				function replaceWith($rootElement, elementsToRemove, newNode) {
					var firstElementToRemove = elementsToRemove[0], removeCount = elementsToRemove.length, parent = firstElementToRemove.parentNode, i, ii;

					if ($rootElement) {
						for ( i = 0, ii = $rootElement.length; i < ii; i++) {
							if ($rootElement[i] == firstElementToRemove) {
								$rootElement[i++] = newNode;
								for (var j = i, j2 = j + removeCount - 1, jj = $rootElement.length; j < jj; j++, j2++) {
									if (j2 < jj) {
										$rootElement[j] = $rootElement[j2];
									} else {
										delete $rootElement[j];
									}
								}
								$rootElement.length -= removeCount - 1;
								break;
							}
						}
					}

					if (parent) {
						parent.replaceChild(newNode, firstElementToRemove);
					}
					var fragment = document.createDocumentFragment();
					fragment.appendChild(firstElementToRemove);
					newNode[jqLite.expando] = firstElementToRemove[jqLite.expando];
					//***jimmy fix class
					safeAddClass(jqLite(newNode), firstElementToRemove.className);

					for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
						var element = elementsToRemove[k];
						jqLite(element).remove();
						// must do this way to clean up expando
						fragment.appendChild(element);
						delete elementsToRemove[k];
					}

					elementsToRemove[0] = newNode;
					elementsToRemove.length = 1;

				}

			}

		}

	}

	/**
	 * Closure compiler type information
	 */
	function nodesetLinkFn(
	/* vx.Scope */scope,
	/* NodeList */nodeList,
	/* Element */rootElement,
	/* function(Function) */boundTranscludeFn) {
	}

	function nodeLinkFn(
	/* nodesetLinkFn */nodesetLinkFn,
	/* vx.Scope */scope,
	/* Node */node,
	/* Element */rootElement,
	/* function(Function) */boundTranscludeFn) {
	}

	////////////////////////////////
	// $RootScopeProvider
	///////////////////////////////

	/**
	 * DESIGN NOTES
	 *
	 * The design decisions behind the scope are heavily favored for speed and memory consumption.
	 *
	 * The typical use of scope is to watch the expressions, which most of the time return the same
	 * value as last time so we optimize the operation.
	 *
	 * Closures construction is expensive in terms of speed as well as memory:
	 *   - No closures, instead use prototypical inheritance for API
	 *   - Internal state needs to be stored on scope directly, which means that private state is
	 *     exposed as $$____ properties
	 *
	 * Loop operations are optimized by using while(count--) { ... }
	 *   - this means that in order to keep the same order of execution as addition we have to add
	 *     items to the array at the beginning (shift) instead of at the end (push)
	 *
	 * Child scopes are created and removed often
	 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
	 *
	 * There are few watches then a lot of observers. This is why you don't want the observer to be
	 * implemented in the same way as watch. Watch requires return of initialization function which
	 * are expensive to construct.
	 */

	/**
	 * @vxdoc object
	 * @name vx.$rootScopeProvider
	 * @description
	 *
	 * Provider for the $rootScope service.
	 */

	/**
	 * @vxdoc function
	 * @name vx.$rootScopeProvider#digestTtl
	 * @methodOf vx.$rootScopeProvider
	 * @description
	 *
	 * Sets the number of digest iterations the scope should attempt to execute before giving up and
	 * assuming that the model is unstable.
	 *
	 * The current default is 10 iterations.
	 *
	 * @param {number} limit The number of digest iterations.
	 */

	/**
	 * @vxdoc object
	 * @name vx.$rootScope
	 * @description
	 *
	 * Every application has a single root {@link vx.$rootScope.Scope scope}.
	 * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide
	 * event processing life-cycle. See {@link guide/scope developer guide on scopes}.
	 */
	function $RootScopeProvider() {
		var TTL = 10, digestTraced;
		this.digestTtl = function(value) {
			if (arguments.length) {
				TTL = value;
			}
			return TTL;
		};

		this.traceDigest = function(yes) {
			digestTraced = !!yes;
		};

		this.$get = [/*'$injector', *//*'$exceptionHandler', */'$log', '$parse', 'dateFilter', '$window', '$locale', '$rootElement', scopeFactory];

		function scopeFactory(/*$injector, *//*$exceptionHandler, */$log, $parse, dateFilter, $window, $locale, $rootElement) {

			/**
			 * @vxdoc function
			 * @name vx.$rootScope.Scope
			 *
			 * @description
			 * A root scope can be retrieved using the {@link vx.$rootScope $rootScope} key from the
			 * {@link AUTO.$injector $injector}. Child scopes are created using the
			 * {@link vx.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
			 * compiled HTML template is executed.)
			 *
			 * Here is a simple scope snippet to show how you can interact with the scope.
			 * <pre>
			 vx.injector(['v']).invoke(function($rootScope) {
			 var scope = $rootScope.$new();
			 scope.salutation = 'Hello';
			 scope.name = 'World';

			 expect(scope.greeting).toEqual(undefined);

			 scope.$watch('name', function() {
			 scope.greeting = scope.salutation + ' ' + scope.name + '!';
			 }); // initialize the watch

			 expect(scope.greeting).toEqual(undefined);
			 scope.name = 'Misko';
			 // still old value, since watches have not been called yet
			 expect(scope.greeting).toEqual(undefined);

			 scope.$digest(); // fire all  the watches
			 expect(scope.greeting).toEqual('Hello Misko!');
			 });
			 * </pre>
			 *
			 * # Inheritance
			 * A scope can inherit from a parent scope, as in this example:
			 * <pre>
			 var parent = $rootScope;
			 var child = parent.$new();

			 parent.salutation = "Hello";
			 child.name = "World";
			 expect(child.salutation).toEqual('Hello');

			 child.salutation = "Welcome";
			 expect(child.salutation).toEqual('Welcome');
			 expect(parent.salutation).toEqual('Hello');
			 * </pre>
			 *
			 *
			 * @param {Object.<string, function()>=} providers Map of service factory which need to be provided
			 *     for the current scope. Defaults to {@link v}.
			 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
			 *     append/override services provided by `providers`. This is handy when unit-testing and having
			 *     the need to override a default service.
			 * @returns {Object} Newly created scope.
			 *
			 */
			function Scope() {
				this.$id = nextUid(scopeId);
				this.$$phase = this.$$watchers = this.$$nextSibling = this.$$prevSibling = this.$$childHead = this.$$childTail = this.$parent = null;
				this['this'] = this.$root = this;
				this.$$destroyed = false;
				this.$$asyncQueue = [];
				this.$$listeners = {};
				this.$$isolateBindings = {};

				this.$repaint = repaintFactory($rootElement);

				this.$window = $window;
				this.$vx = vx;
				this.$now = function(fmt) {
					if (fmt === undefined || fmt === false)
						return now();
					if (fmt === true)
						return dateFilter(now(), 'yyyy-MM-ddTHH:mm:ss.sssZ');
					return dateFilter(now(), fmt);
				};

				this.$field = function(name) {
					return $locale.FIELDS[name] || name;
				};
				this.$msg = function(name) {
					return $locale.MESSAGES[name] || name;
				};
			}

			/**
			 * @vxdoc property
			 * @name vx.$rootScope.Scope#$id
			 * @propertyOf vx.$rootScope.Scope
			 * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
			 *   debugging.
			 */
			Scope.prototype = {
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$new
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Creates a new child {@link vx.$rootScope.Scope scope}.
				 *
				 * The parent scope will propagate the {@link vx.$rootScope.Scope#$digest $digest()} and
				 * {@link vx.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope
				 * hierarchy using {@link vx.$rootScope.Scope#$destroy $destroy()}.
				 *
				 * {@link vx.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for
				 * the scope and its child scopes to be permanently detached from the parent and thus stop
				 * participating in model change detection and listener notification by invoking.
				 *
				 * @param {boolean} isolate if true then the scope does not prototypically inherit from the
				 *         parent scope. The scope is isolated, as it can not see parent scope properties.
				 *         When creating widgets it is useful for the widget to not accidentally read parent
				 *         state.
				 *
				 * @returns {Object} The newly created child scope.
				 *
				 */
				$new : function(isolate) {
					var Child, child;
					if (isolate) {
						child = new Scope();
						child.$root = this.$root;
					} else {
						Child = function() {
						};
						// should be anonymous; This is so that when the minifier munges
						// the name it does not become random set of chars. These will then show up as class
						// name in the debugger.
						Child.prototype = this;
						child = new Child();
						child.$id = nextUid(scopeId);
					}
					child['this'] = child;
					child.$$listeners = {};
					child.$parent = this;
					child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
					child.$$prevSibling = this.$$childTail;
					if (this.$$childHead) {
						this.$$childTail.$$nextSibling = child;
						this.$$childTail = child;
					} else {
						this.$$childHead = this.$$childTail = child;
					}

					return child;
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$watch
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
				 *
				 * - The `watchExpression` is called on every call to {@link vx.$rootScope.Scope#$digest $digest()} and
				 *   should return the value which will be watched. (Since {@link vx.$rootScope.Scope#$digest $digest()}
				 *   reruns when it detects changes the `watchExpression` can execute multiple times per
				 *   {@link vx.$rootScope.Scope#$digest $digest()} and should be idempotent.)
				 * - The `listener` is called only when the value from the current `watchExpression` and the
				 *   previous call to `watchExpression` are not equal (with the exception of the initial run,
				 *   see below). The inequality is determined according to
				 *   {@link vx.equals} function. To save the value of the object for later comparison, the
				 *   {@link vx.copy} function is used. It also means that watching complex options will
				 *   have adverse memory and performance implications.
				 * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This
				 *   is achieved by rerunning the watchers until no changes are detected. The rerun iteration
				 *   limit is 10 to prevent an infinite loop deadlock.
				 *
				 *
				 * If you want to be notified whenever {@link vx.$rootScope.Scope#$digest $digest} is called,
				 * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
				 * can execute multiple times per {@link vx.$rootScope.Scope#$digest $digest} cycle when a change is
				 * detected, be prepared for multiple calls to your listener.)
				 *
				 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
				 * (via {@link vx.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
				 * watcher. In rare cases, this is undesirable because the listener is called when the result
				 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
				 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
				 * listener was called due to initialization.
				 *
				 *
				 * # Example
				 * <pre>
				 // let's assume that scope was dependency injected as the $rootScope
				 var scope = $rootScope;
				 scope.name = 'misko';
				 scope.counter = 0;

				 expect(scope.counter).toEqual(0);
				 scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
				 expect(scope.counter).toEqual(0);

				 scope.$digest();
				 // no variable change
				 expect(scope.counter).toEqual(0);

				 scope.name = 'adam';
				 scope.$digest();
				 expect(scope.counter).toEqual(1);
				 * </pre>
				 *
				 *
				 *
				 * @param {(function()|string)} watchExpression Expression that is evaluated on each
				 *    {@link vx.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a
				 *    call to the `listener`.
				 *
				 *    - `string`: Evaluated as {@link guide/expression expression}
				 *    - `function(scope)`: called with current `scope` as a parameter.
				 * @param {(function()|string)=} listener Callback called whenever the return value of
				 *   the `watchExpression` changes.
				 *
				 *    - `string`: Evaluated as {@link guide/expression expression}
				 *    - `function(newValue, oldValue, scope)`: called with current and previous values as parameters.
				 *
				 * @param {boolean=} objectEquality Compare object for equality rather than for reference.
				 * @returns {function()} Returns a deregistration function for this listener.
				 */
				$watch : function(watchExp, listener, objectEquality, runFirst) {
					var scope = this, get = compileToFn(watchExp, 'watch'), array = scope.$$watchers, watcher = {
						fn : listener,
						last : initWatchVal,
						get : get,
						exp : watchExp,
						eq : !!objectEquality
					};

					// in the case user pass string, we need to compile it, do we really need this ?
					if (!isFunction(listener)) {
						var listenFn = compileToFn(listener || noop, 'listener');
						watcher.fn = function(newVal, oldVal, scope) {
							listenFn(scope);
						};
					}

					if ( typeof watchExp == 'string' && get.constant) {
						var originalFn = watcher.fn;
						watcher.fn = function(newVal, oldVal, scope) {
							originalFn.call(this, newVal, oldVal, scope);
							arrayRemove(array, watcher);
						};
					}

					if (!array) {
						array = scope.$$watchers = [];
					}
					// we use unshift since we use a while loop in $digest for speed.
					// the while loop reads in reverse order.
					array.unshift(watcher);

					if (runFirst) {
						var value = watcher.get(scope);
						watcher.last = watcher.eq ? copy(value) : value;
						watcher.fn(value, null, scope);
					}

					return function() {
						arrayRemove(array, watcher);
					};
				},

				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$watchCollection
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Shallow watches the properties of an object and fires whenever any of the properties change
				 * (for arrays this implies watching the array items, for object maps this implies watching the properties).
				 * If a change is detected the `listener` callback is fired.
				 *
				 * - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to
				 *   see if any items have been added, removed, or moved.
				 * - The `listener` is called whenever anything within the `obj` has changed. Examples include adding new items
				 *   into the object or array, removing and moving items around.
				 *
				 *
				 * # Example
				 * <pre>
				 $scope.names = ['igor', 'matias', 'misko', 'james'];
				 $scope.dataCount = 4;

				 $scope.$watchCollection('names', function(newNames, oldNames) {
				 $scope.dataCount = newNames.length;
				 });

				 expect($scope.dataCount).toEqual(4);
				 $scope.$digest();

				 //still at 4 ... no changes
				 expect($scope.dataCount).toEqual(4);

				 $scope.names.pop();
				 $scope.$digest();

				 //now there's been a change
				 expect($scope.dataCount).toEqual(3);
				 * </pre>
				 *
				 *
				 * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value
				 *    should evaluate to an object or an array which is observed on each
				 *    {@link vx.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger
				 *    a call to the `listener`.
				 *
				 * @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both
				 *    the `newCollection` and `oldCollection` as parameters.
				 *    The `newCollection` object is the newly modified data obtained from the `obj` expression and the
				 *    `oldCollection` object is a copy of the former collection data.
				 *    The `scope` refers to the current scope.
				 *
				 * @returns {function()} Returns a de-registration function for this listener. When the de-registration function is executed
				 * then the internal watch operation is terminated.
				 */
				$watchCollection : function(obj, listener, runFirst) {
					var self = this;
					var oldValue;
					var newValue;
					var changeDetected = 0;
					var objGetter = $parse(obj);
					var internalArray = [];
					var internalObject = {};
					var oldLength = 0;

					function $watchCollectionWatch() {
						newValue = objGetter(self);
						var newLength, key;

						if (!isObject(newValue)) {
							if (oldValue !== newValue) {
								oldValue = newValue;
								changeDetected++;
							}
						} else if (isArrayLike(newValue)) {
							if (oldValue !== internalArray) {
								// we are transitioning from something which was not an array into array.
								oldValue = internalArray;
								oldLength = oldValue.length = 0;
								changeDetected++;
							}

							newLength = newValue.length;

							if (oldLength !== newLength) {
								// if lengths do not match we need to trigger change notification
								changeDetected++;
								oldValue.length = oldLength = newLength;
							}
							// copy the items to oldValue and look for changes.
							for (var i = 0; i < newLength; i++) {
								if (oldValue[i] !== newValue[i]) {
									changeDetected++;
									oldValue[i] = newValue[i];
								}
							}
						} else {
							if (oldValue !== internalObject) {
								// we are transitioning from something which was not an object into object.
								oldValue = internalObject = {};
								oldLength = 0;
								changeDetected++;
							}
							// copy the items to oldValue and look for changes.
							newLength = 0;
							for (key in newValue) {
								if (newValue.hasOwnProperty(key)) {
									newLength++;
									if (oldValue.hasOwnProperty(key)) {
										if (oldValue[key] !== newValue[key]) {
											changeDetected++;
											oldValue[key] = newValue[key];
										}
									} else {
										oldLength++;
										oldValue[key] = newValue[key];
										changeDetected++;
									}
								}
							}
							if (oldLength > newLength) {
								// we used to have more keys, need to find them and destroy them.
								changeDetected++;
								for (key in oldValue) {
									if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
										oldLength--;
										delete oldValue[key];
									}
								}
							}
						}
						return changeDetected;
					}

					function $watchCollectionAction() {
						listener(newValue, oldValue, self);
					}

					return this.$watch($watchCollectionWatch, $watchCollectionAction, false, runFirst);
				},

				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$digest
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Processes all of the {@link vx.$rootScope.Scope#$watch watchers} of the current scope and its children.
				 * Because a {@link vx.$rootScope.Scope#$watch watcher}'s listener can change the model, the
				 * `$digest()` keeps calling the {@link vx.$rootScope.Scope#$watch watchers} until no more listeners are
				 * firing. This means that it is possible to get into an infinite loop. This function will throw
				 * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10.
				 *
				 * Usually you don't call `$digest()` directly in
				 * {@link vx.directive:vController controllers} or in
				 * {@link vx.$compileProvider#directive directives}.
				 * Instead a call to {@link vx.$rootScope.Scope#$apply $apply()} (typically from within a
				 * {@link vx.$compileProvider#directive directives}) will force a `$digest()`.
				 *
				 * If you want to be notified whenever `$digest()` is called,
				 * you can register a `watchExpression` function  with {@link vx.$rootScope.Scope#$watch $watch()}
				 * with no `listener`.
				 *
				 * You may have a need to call `$digest()` from within unit-tests, to simulate the scope
				 * life-cycle.
				 *
				 * # Example
				 * <pre>
				 var scope = ...;
				 scope.name = 'misko';
				 scope.counter = 0;

				 expect(scope.counter).toEqual(0);
				 scope.$watch('name', function(newValue, oldValue) {
				 scope.counter = scope.counter + 1;
				 });
				 expect(scope.counter).toEqual(0);

				 scope.$digest();
				 // no variable change
				 expect(scope.counter).toEqual(0);

				 scope.name = 'adam';
				 scope.$digest();
				 expect(scope.counter).toEqual(1);
				 * </pre>
				 *
				 */
				$digest : function(cause) {
					var watch, value, last, watchers, asyncQueue = this.$$asyncQueue, length, dirty, ttl = TTL, next, //
					current, target = this, watchLog = [], logIdx, logMsg;

					beginPhase(target, '$digest');
					var startMs = now();
					try {
						do {// "while dirty" loop
							dirty = false;
							current = target;

							while (asyncQueue.length) {
								//try {
								current.$eval(asyncQueue.shift());
								//} catch (e) {
								//  $log.error(e);
								//	$exceptionHandler(e);
								//}
							}

							do {// "traverse the scopes" loop
								if (( watchers = current.$$watchers)) {
									// process our watches
									length = watchers.length;
									while (length--) {
										//try {
										watch = watchers[length];
										// Most common watches are on primitives, in which case we can short
										// circuit it with === operator, only when === fails do we use .equals
										if (( value = watch.get(current)) !== ( last = watch.last) && !(watch.eq ? equals(value, last) : //
										( typeof value == 'number' && typeof last == 'number' && isNaN(value) && isNaN(last)))) {
											dirty = true;
											watch.last = watch.eq ? copy(value) : value;
											watch.fn(value, ((last === initWatchVal) ? value : last), current);
											if (ttl < 5) {
												logIdx = 4 - ttl;
												if (!watchLog[logIdx])
													watchLog[logIdx] = [];
												logMsg = (isFunction(watch.exp)) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp;
												logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
												watchLog[logIdx].push(logMsg);
											}
										}
										//} catch (e) {
										//  $log.error(e);
										//	$exceptionHandler(e);
										//}
									}
								}

								// Insanity Warning : scope depth-first traversal
								// yes, this code is a bit crazy, but it works and we have tests to prove it!
								// this piece should be kept in sync with the traversal in $broadcast
								if (!( next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
									while (current !== target && !( next = current.$$nextSibling)) {
										current = current.$parent;
									}
								}
							} while ((current = next));

							if (dirty && !(ttl--)) {
								clearPhase(target);
								throw vError(45, '{0} $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: {1}', TTL, toJson(watchLog));
							}
						} while (dirty || asyncQueue.length);

					} finally {
						if (digestTraced) {
							$log.debug('$digest (' + (now() - startMs) + 'ms, iteration: ' + (TTL - ttl + 1) + ') in scope ' + this.$id + //
							(this.hasOwnProperty('$$viewport') ? ' for viewport ' + this.$$viewport : '') + ( cause ? ' by ' + cause : ''));
						}
						clearPhase(target);

						target.$repaint();
					}
				},
				/**
				 * @vxdoc event
				 * @name vx.$rootScope.Scope#$destroy
				 * @eventOf vx.$rootScope.Scope
				 * @eventType broadcast on scope being destroyed
				 *
				 * @description
				 * Broadcasted when a scope and its children are being destroyed.
				 */

				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$destroy
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Removes the current scope (and all of its children) from the parent scope. Removal implies
				 * that calls to {@link vx.$rootScope.Scope#$digest $digest()} will no longer
				 * propagate to the current scope and its children. Removal also implies that the current
				 * scope is eligible for garbage collection.
				 *
				 * The `$destroy()` is usually used by directives such as
				 * {@link vx.directive:vRepeat vRepeat} for managing the
				 * unrolling of the loop.
				 *
				 * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope.
				 * Application code can register a `$destroy` event handler that will give it chance to
				 * perform any necessary cleanup.
				 */
				$destroy : function() {
					if ($rootScope == this || this.$$destroyed)
						return;
					// we can't remove the root node;
					var parent = this.$parent;

					this.$broadcast('$destroy');
					this.$$destroyed = true;

					var target = this, current = target, next = target, i;
					//down while you can, then up and next sibling or up and next sibling until back at root
					do {
						current = next;
						delete current.$$applied;
						// for viewport appliedScope.
						delete current.$$watchers;
						delete current.$$asyncQueue;
						delete current.$$listeners;
						delete current.$$isolateBindings;

						for (i in current) {
							if (current.hasOwnProperty(i) && i.substr(0, 2) !== '$$' && i !== '$parent' && i !== '$root' && i !== '$id') {
								delete current[i];
							}
						}

						// Insanity Warning : scope depth-first traversal
						// yes, this code is a bit crazy, but it works and we have tests to prove it!
						// this piece should be kept in sync with the traversal in $digest
						if (!( next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
							while (current !== target && !( next = current.$$nextSibling)) {
								current = current.$parent;
							}
						}
					} while ((current = next));

					if (parent.$$childHead == this)
						parent.$$childHead = this.$$nextSibling;
					if (parent.$$childTail == this)
						parent.$$childTail = this.$$prevSibling;
					if (this.$$prevSibling)
						this.$$prevSibling.$$nextSibling = this.$$nextSibling;
					if (this.$$nextSibling)
						this.$$nextSibling.$$prevSibling = this.$$prevSibling;

					// This is bogus code that works around Chrome's GC leak
					this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = this.$$childTail = null;

				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$eval
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Executes the `expression` on the current scope returning the result. Any exceptions in the
				 * expression are propagated (uncaught). This is useful when evaluating vx expressions.
				 *
				 * # Example
				 * <pre>
				 var scope = vx.$rootScope.Scope();
				 scope.a = 1;
				 scope.b = 2;

				 expect(scope.$eval('a+b')).toEqual(3);
				 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
				 * </pre>
				 *
				 * @param {(string|function())=} expression An vx expression to be executed.
				 *
				 *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
				 *    - `function(scope)`: execute the function with the current `scope` parameter.
				 *
				 * @returns {*} The result of evaluating the expression.
				 */
				$eval : function(expr, locals) {
					return $parse(expr)(this, locals);
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$evalAsync
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Executes the expression on the current scope at a later point in time.
				 *
				 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:
				 *
				 *   - it will execute in the current script execution context (before any DOM rendering).
				 *   - at least one {@link vx.$rootScope.Scope#$digest $digest cycle} will be performed after
				 *     `expression` execution.
				 *
				 * Any exceptions from the execution of the expression are forwarded to the
				 * {@link vx.$exceptionHandler $exceptionHandler} service.
				 *
				 * @param {(string|function())=} expression An vx expression to be executed.
				 *
				 *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
				 *    - `function(scope)`: execute the function with the current `scope` parameter.
				 *
				 */
				$evalAsync : function(expr) {
					this.$$asyncQueue.push(expr);
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$apply
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * `$apply()` is used to execute an expression in vx from outside of the vx framework.
				 * (For example from browser DOM events, setTimeout, XHR or third party libraries).
				 * Because we are calling into the vx framework we need to perform proper scope life-cycle
				 * of {@link vx.$exceptionHandler exception handling},
				 * {@link vx.$rootScope.Scope#$digest executing watches}.
				 *
				 * ## Life cycle
				 *
				 * # Pseudo-Code of `$apply()`
				 * <pre>
				 function $apply(expr) {
				 try {
				 return $eval(expr);
				 } catch (e) {
				 $exceptionHandler(e);
				 } finally {
				 $root.$digest();
				 }
				 }
				 * </pre>
				 *
				 *
				 * Scope's `$apply()` method transitions through the following stages:
				 *
				 * 1. The {@link guide/expression expression} is executed using the
				 *    {@link vx.$rootScope.Scope#$eval $eval()} method.
				 * 2. Any exceptions from the execution of the expression are forwarded to the
				 *    {@link vx.$exceptionHandler $exceptionHandler} service.
				 * 3. The {@link vx.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression
				 *    was executed using the {@link vx.$rootScope.Scope#$digest $digest()} method.
				 *
				 *
				 * @param {(string|function())=} exp An vx expression to be executed.
				 *
				 *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
				 *    - `function(scope)`: execute the function with current `scope` parameter.
				 *
				 * @returns {*} The result of evaluating the expression.
				 */
				$apply : function(expr, cause) {
					try {
						beginPhase(this, '$apply');
						return this.$eval(expr);
					} catch (e) {
						$log.error(formatError(e));
						//$exceptionHandler(e);
					} finally {
						clearPhase(this);
						try {
							var appliedScope = this.$$applied || $rootScope;
							appliedScope.$digest(cause);
						} catch (e) {
							$log.error(formatError(e));
							//$exceptionHandler(e);
							//throw e;
						}
					}
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$on
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Listens on events of a given type. See {@link vx.$rootScope.Scope#$emit $emit} for discussion of
				 * event life cycle.
				 *
				 * @param {string} name Event name to listen on.
				 * @param {function(event, args...)} listener Function to call when the event is emitted.
				 * @returns {function()} Returns a deregistration function for this listener.
				 *
				 * The event listener function format is: `function(event, args...)`. The `event` object passed into the
				 * listener has the following attributes
				 *
				 *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
				 *   - `currentScope` - `{Scope}`: the current scope which is handling the event.
				 *   - `name` - `{string}`: Name of the event.
				 *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event propagation
				 *     (available only for events that were `$emit`-ed).
				 *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
				 *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
				 */
				$on : function(name, listener) {
					var namedListeners = this.$$listeners[name];
					if (!namedListeners) {
						this.$$listeners[name] = namedListeners = [];
					}
					namedListeners.push(listener);
					return function() {
						namedListeners[indexOf(namedListeners, listener)] = null;
					};
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$emit
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Dispatches an event `name` upwards through the scope hierarchy notifying the
				 * registered {@link vx.$rootScope.Scope#$on} listeners.
				 *
				 * The event life cycle starts at the scope on which `$emit` was called. All
				 * {@link vx.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified.
				 * Afterwards, the event traverses upwards toward the root scope and calls all registered
				 * listeners along the way. The event will stop propagating if one of the listeners cancels it.
				 *
				 * Any exception emitted from the {@link vx.$rootScope.Scope#$on listeners} will be passed
				 * onto the {@link vx.$exceptionHandler $exceptionHandler} service.
				 *
				 * @param {string} name Event name to emit.
				 * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
				 * @return {Object} Event object, see {@link vx.$rootScope.Scope#$on}
				 */
				$emit : function(name, args) {
					var empty = [], namedListeners, scope = this, stopPropagation = false, event = {
						name : name,
						targetScope : scope,
						stopPropagation : function() {
							stopPropagation = true;
						},
						preventDefault : function() {
							event.defaultPrevented = true;
						},
						defaultPrevented : false
					}, listenerArgs = concat([event], arguments, 1), i, length;

					do {
						namedListeners = scope.$$listeners[name] || empty;
						event.currentScope = scope;
						for ( i = 0, length = namedListeners.length; i < length; i++) {
							// if listeners were deregistered, defragment the array
							if (!namedListeners[i]) {
								namedListeners.splice(i, 1);
								i--;
								length--;
								continue;
							}
							try {
								_fastInvoke(namedListeners[i], null, listenerArgs);
								if (stopPropagation)
									return event;
							} catch (e) {
								$log.error(formatError(e));
								//$exceptionHandler(e);
							}
						}
						//traverse upwards
						scope = scope.$parent;
					} while (scope);
					return event;
				},
				/**
				 * @vxdoc function
				 * @name vx.$rootScope.Scope#$broadcast
				 * @methodOf vx.$rootScope.Scope
				 * @function
				 *
				 * @description
				 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
				 * registered {@link vx.$rootScope.Scope#$on} listeners.
				 *
				 * The event life cycle starts at the scope on which `$broadcast` was called. All
				 * {@link vx.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified.
				 * Afterwards, the event propagates to all direct and indirect scopes of the current scope and
				 * calls all registered listeners along the way. The event cannot be canceled.
				 *
				 * Any exception emitted from the {@link vx.$rootScope.Scope#$on listeners} will be passed
				 * onto the {@link vx.$exceptionHandler $exceptionHandler} service.
				 *
				 * @param {string} name Event name to broadcast.
				 * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
				 * @return {Object} Event object, see {@link vx.$rootScope.Scope#$on}
				 */
				$broadcast : function(name, args) {
					var target = this, current = target, next = target, event = {
						name : name,
						targetScope : target,
						preventDefault : function() {
							event.defaultPrevented = true;
						},
						defaultPrevented : false
					}, listenerArgs = concat([event], arguments, 1), listeners, i, length;

					//down while you can, then up and next sibling or up and next sibling until back at root
					do {
						current = next;
						event.currentScope = current;
						listeners = current.$$listeners[name] || [];
						for ( i = 0, length = listeners.length; i < length; i++) {
							// if listeners were deregistered, defragment the array
							if (!listeners[i]) {
								listeners.splice(i, 1);
								i--;
								length--;
								continue;
							}

							try {
								_fastInvoke(listeners[i], null, listenerArgs);
							} catch(e) {
								$log.error(formatError(e));
								//$exceptionHandler(e);
							}
						}
						// Insanity Warning : scope depth-first traversal
						// yes, this code is a bit crazy, but it works and we have tests to prove it!
						// this piece should be kept in sync with the traversal in $digest
						if (!( next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
							while (current !== target && !( next = current.$$nextSibling)) {
								current = current.$parent;
							}
						}
					} while ((current = next));
					return event;
				}
			};
			var $rootScope = new Scope();

			return $rootScope;
			// -------------------------------------------

			function beginPhase(scope, phase) {
				if (scope.$$phase) {
					throw vError(46, '{0} already in progress', scope.$$phase);
				}
				scope.$$phase = phase;
			}

			function clearPhase(scope) {
				scope.$$phase = null;
			}

			function compileToFn(exp, name) {
				var fn = $parse(exp);
				assertArgFn(fn, name);
				return fn;
			}

			/**
			 * function used as an initial value for watchers.
			 * because it's unique we can easily tell it apart from other values
			 */
			function initWatchVal() {
			}

		}

	}

	function injectRemote(el, locals, expr, submit, $$remote) {
		locals = locals || {};
		if (/\$remote\./.test(expr)) {
			var $remote = el.data('$remote');
			if (!$remote) {
				$remote = $$remote(el, submit);
				el.data('$remote', $remote);
			}
			locals.$remote = $remote;
		}
		return locals;
	}

	// $$Remote service
	function $$RemoteProvider() {
		var getFailData, errorTag = '$error', callbacks = {},useEncrypt = false;

		this.setErrorCallback = function(fn) {
			// fn(response.data, response.status, response.headers, config);
			// if error return error object, otherwise return null
			getFailData = fn;
		};

		this.setErrorTag = function(n) {
			errorTag = n;
		};

		this.addCallback = function(name, callback) {
			// fn(data, ...)
			callbacks[name] = callback;
		};
		this.useEncrypt = function(flag){
			useEncrypt = !!flag;
		};
		// this.setEncryptKey = function(key){
			// encryptKey = key || encryptKey;
		// };

		this.$get = ['$http', '$targets', '$log', remoteFactory];

		function remoteFactory($http, $targets, $log) {

			// override $http in viewport
			function wrapHttp(scope, ajaxPort, refer) {
				function $config(config) {
					return extend(config || {}, {
						$scope : scope,
						ajaxPort : ajaxPort,
						$referer : refer
					});
				}

				var $$http = function(config) {
					return $http($config(config));
				};
				$$http.get = function(url, config) {
					return $http.get(url, $config(config));
				};
				$$http['delete'] = function(url, config) {
					return $http['delete'](url, $config(config));
				};
				$$http.head = function(url, config) {
					return $http.head(url, $config(config));
				};
				$$http.jsonp = function(url, config) {
					return $http.jsonp(url, $config(config));
				};
				$$http.post = function(url, data, config) {
					return $http.post(url, data, $config(config));
				};
				$$http.put = function(url, data, config) {
					return $http.put(url, data, $config(config));
				};

				return $$http;
			}

			return function($element, submit, ajaxPort) {
				var view = $element.controller('vViewport');
				var scope = view && view.$activeElement.scope() || $element.scope();
				var refer = view && view.$rootUrl || '/';

				$element = null;

				return {
					$http : function() {
						return view && wrapHttp(scope, ajaxPort, refer) || $http;
					},
					get : function(url, data, okTag, config) {
						return withParams('get', url, data, okTag, config);
					},
					'delete' : function(url, data, okTag, config) {
						return withParams('delete', url, data, okTag, config);
					},
					head : function(url, data, okTag, config) {
						return withParams('head', url, data, okTag, config);
					},
					jsonp : function(url, data, okTag, config) {
						return withParams('jsonp', url, data, okTag, config);
					},
					post : function(url, data, okTag, config) {
						return withNoParams('post', url, data, okTag, config);
					},
					put : function(url, data, okTag, config) {
						return withNoParams('put', url, data, okTag, config);
					}
				};

				function withParams(name, url, data, okTag, config) {
					var params;
					if (isString(data) || isFunction(data)) {
						okTag = data;
						config = okTag;
					} else {
						params = data;
					}
					return proxy(url, params, null, okTag, config, name);
				}

				function withNoParams(name, url, data, okTag, config) {
					return proxy(url, null, data, okTag, config, name);
				}

				function proxy(url, params, data, okTag, config, name) {
					
					$log.debug('remote ' + name + ': ' + url + ( data ? ', with data: ' + toJson(data) : ''));
					/**
					 * encrypt request data if useEncrypt and method=post
					 * add by ljw 
					 */
					if(useEncrypt && name === "post"){
						data = encryptDES(window.encryptKey,toJson(data));
						$log.debug('remote ' + name + ': ' + url + ( data ? ', with encrypt data: ' + data : ''));
					}
					config = extend(config || {}, {
						method : name,
						url : url,
						params : params,
						data : data,
						$scope : scope,
						ajaxPort : ajaxPort,
						$referer : refer
					});
					
					if (view)
						view.$$remoteTouch(submit);

					var failTag = errorTag, start = now(), http = $http(config), error;

					// clear error
					putData(failTag, null);

					http.then(function(response) {
						success(response.data, response.status, response.headers, config);
					}, function(response) {
						fail(response.data, response.status, response.headers, config);
					});

					function success(data, status, headers, config) {
						//success: status[200, 300)
						/**
						 * decrypt response data
						 * add by ljw 
						 */
						if(useEncrypt && name === "post"){
							data = decryptDES(window.encryptKey,data);
							var JSON_START = /^\s*(\[|\{[^\{])/, JSON_END = /[\}\]]\s*$/, PROTECTION_PREFIX = /^\)\]\}',?\n/;
							data = data.replace(PROTECTION_PREFIX, '');
							if (JSON_START.test(data) && JSON_END.test(data))
								data = fromJson(data, true);
						}
						error = getFailData && getFailData(data, status, headers, config);
						if (error) {
							$log.error('remote receive(' + (now() - start) + 'ms): ' + url + ', with error: ' + toJson(error));
							putData(failTag, error);
							scope.$root.$broadcast('$remoteError', url, error);
						} else {
							$log.debug('remote receive(' + (now() - start) + 'ms): ' + url + ( data ? ', with data: ' + toJson(data) : ''));
							if (okTag) {
								putData(okTag, data);
							} else {
								populate(scope, data);
							}
							scope.$root.$broadcast('$remoteSuccess', url, data);
						}
					}

					function fail(data, status, headers, config) {
						//error: other status
						error = getFailData && getFailData(data, status, headers, config);
						if (error) {
							$log.error('remote receive(' + (now() - start) + 'ms): ' + url + ', with error: ' + toJson(error));
							putData(failTag, error);
							scope.$root.$broadcast('$remoteError', url, error);
						}
					}

					var ret = {
						forward : function forward() {
							var args;
							if (arguments.length === 1) {
								if (!view)
									throw vError(47, 'no viewport, require target specified');
								args = [view, arguments[0]];
							} else {
								args = arguments;
							}
							http.then(function() {
								if (!error)
									_fastInvoke($targets, null, args);
							});
							return this;
						}
					};

					forEach(callbacks, function(fn, name) {
						ret[name] = function() {
							var args = _slice.call(arguments);
							http.then(function(response) {
								if (!error) {
									var arg = [response.data];
									arg = arg.concat(args);
									_fastInvoke(fn, scope, arg);
								}
							});
							return ret;
						};
					});

					return ret;

				}

				function putData(tag, data) {
					if (isFunction(tag))
						return tag(data);

					if (!isString(tag))
						throw vError(48, 'tag must be String or function(data)');

					var s = scope, name = tag;
					if (name.charAt(0) === '/') {
						name = name.substring(1);
						s = s.$root;
					} else {
						while (s && name.indexOf('../') === 0) {
							name = name.substring(3);
							s = s.$parent;
							if (!s)
								throw vError(49, 'invalid tag, wrong parent level, scope.$parent is null');
						}
					}
					if (name.indexOf('/') != -1)
						throw vError(50, 'invalid tag, require startWith "/" , "../" or none: {0}', tag);
					if (name) {
						s[name] = data;
					} else {
						populate(s, data);
					}
				}

				function populate(scope, data) {
					if (isObject(data) && !isArray(data)) {
						for (var key in data)
						if (data.hasOwnProperty(key)) {
							scope[key] = data[key];
						}
					} else {
						throw vError(51, "only object data {} could populate");
					}
				}

			};
		}

	}

	////////////////////////////////
	// $HttpProvider
	///////////////////////////////

	/**
	 * @vxdoc function
	 * @name vx.$http
	 * @requires $httpBackend
	 * @requires $browser
	 * @requires $cacheFactory
	 * @requires $rootScope
	 * @requires $q
	 * @requires $injector
	 *
	 * @description
	 * The `$http` service is a core VX service that facilitates communication with the remote
	 * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
	 * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
	 *
	 * For unit testing applications that use `$http` service, see
	 * {@link vMock.$httpBackend $httpBackend mock}.
	 *
	 * For a higher level of abstraction, please check out the {@link vResource.$resource
	 * $resource} service.
	 *
	 * The $http API is based on the {@link vx.$q deferred/promise APIs} exposed by
	 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage,
	 * it is important to familiarize yourself with these APIs and the guarantees they provide.
	 *
	 *
	 * # General usage
	 * The `$http` service is a function which takes a single argument — a configuration object —
	 * that is used to generate an HTTP request and returns  a {@link vx.$q promise}
	 * with two $http specific methods: `success` and `error`.
	 *
	 * <pre>
	 *   $http({method: 'GET', url: '/someUrl'}).
	 *     success(function(data, status, headers, config) {
	 *       // this callback will be called asynchronously
	 *       // when the response is available
	 *     }).
	 *     error(function(data, status, headers, config) {
	 *       // called asynchronously if an error occurs
	 *       // or server returns response with an error status.
	 *     });
	 * </pre>
	 *
	 * Since the returned value of calling the $http function is a `promise`, you can also use
	 * the `then` method to register callbacks, and these callbacks will receive a single argument –
	 * an object representing the response. See the API signature and type info below for more
	 * details.
	 *
	 * A response status code between 200 and 299 is considered a success status and
	 * will result in the success callback being called. Note that if the response is a redirect,
	 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
	 * called for such responses.
	 *
	 * # Shortcut methods
	 *
	 * Since all invocations of the $http service require passing in an HTTP method and URL, and
	 * POST/PUT requests require request data to be provided as well, shortcut methods
	 * were created:
	 *
	 * <pre>
	 *   $http.get('/someUrl').success(successCallback);
	 *   $http.post('/someUrl', data).success(successCallback);
	 * </pre>
	 *
	 * Complete list of shortcut methods:
	 *
	 * - {@link vx.$http#get $http.get}
	 * - {@link vx.$http#head $http.head}
	 * - {@link vx.$http#post $http.post}
	 * - {@link vx.$http#put $http.put}
	 * - {@link vx.$http#delete $http.delete}
	 * - {@link vx.$http#jsonp $http.jsonp}
	 *
	 *
	 * # Setting HTTP Headers
	 *
	 * The $http service will automatically add certain HTTP headers to all requests. These defaults
	 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
	 * object, which currently contains this default configuration:
	 *
	 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
	 *   - `Accept: application/json, text/plain, * / *`
	 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
	 *   - `Content-Type: application/json`
	 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
	 *   - `Content-Type: application/json`
	 *
	 * To add or overwrite these defaults, simply add or remove a property from these configuration
	 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
	 * with the lowercased HTTP method name as the key, e.g.
	 * `$httpProvider.defaults.headers.get['My-Header']='value'`.
	 *
	 * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
	 * fashion.
	 *
	 *
	 * # Transforming Requests and Responses
	 *
	 * Both requests and responses can be transformed using transform functions. By default, VX
	 * applies these transformations:
	 *
	 * Request transformations:
	 *
	 * - If the `data` property of the request configuration object contains an object, serialize it into
	 *   JSON format.
	 *
	 * Response transformations:
	 *
	 *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
	 *  - If JSON response is detected, deserialize it using a JSON parser.
	 *
	 * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
	 * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
	 * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
	 * transformation chain. You can also decide to completely override any default transformations by assigning your
	 * transformation functions to these properties directly without the array wrapper.
	 *
	 * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
	 * `transformResponse` properties of the configuration object passed into `$http`.
	 *
	 *
	 * # Caching
	 *
	 * To enable caching, set the configuration property `cache` to `true`. When the cache is
	 * enabled, `$http` stores the response from the server in local cache. Next time the
	 * response is served from the cache without sending a request to the server.
	 *
	 * Note that even if the response is served from cache, delivery of the data is asynchronous in
	 * the same way that real requests are.
	 *
	 * If there are multiple GET requests for the same URL that should be cached using the same
	 * cache, but the cache is not populated yet, only one request to the server will be made and
	 * the remaining requests will be fulfilled using the response from the first request.
	 *
	 * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
	 * To skip it, set configuration property `cache` to `false`.
	 *
	 * # Interceptors
	 *
	 * Before you start creating interceptors, be sure to understand the
	 * {@link vx.$q $q and deferred/promise APIs}.
	 *
	 * For purposes of global error handling, authentication, or any kind of synchronous or
	 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
	 * able to intercept requests before they are handed to the server and
	 * responses before they are handed over to the application code that
	 * initiated these requests. The interceptors leverage the {@link vx.$q
	 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
	 *
	 * The interceptors are service factories that are registered with the `$httpProvider` by
	 * adding them to the `$httpProvider.interceptors` array. The factory is called and
	 * injected with dependencies (if specified) and returns the interceptor.
	 *
	 * There are two kinds of interceptors (and two kinds of rejection interceptors):
	 *
	 *   * `request`: interceptors get called with http `config` object. The function is free to modify
	 *     the `config` or create a new one. The function needs to return the `config` directly or as a
	 *     promise.
	 *   * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved
	 *      with a rejection.
	 *   * `response`: interceptors get called with http `response` object. The function is free to modify
	 *     the `response` or create a new one. The function needs to return the `response` directly or as a
	 *     promise.
	 *   * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved
	 *      with a rejection.
	 *
	 *
	 * <pre>
	 *   // register the interceptor as a service
	 *   $provide.factory('myHttpInterceptor', function(dependency1, dependency2) {
	 *     return {
	 *       // optional method
	 *       'request': function(config, $q) {
	 *         // do something on success
	 *         return config || $q.when(config);
	 *       },
	 *
	 *       // optional method
	 *      'requestError': function(rejection, $q) {
	 *         // do something on error
	 *         if (canRecover(rejection)) {
	 *           return responseOrNewPromise
	 *         }
	 *         return $q.reject(rejection);
	 *       },
	 *
	 *
	 *
	 *       // optional method
	 *       'response': function(response, $q) {
	 *         // do something on success
	 *         return response || $q.when(response);
	 *       },
	 *
	 *       // optional method
	 *      'responseError': function(rejection, $q) {
	 *         // do something on error
	 *         if (canRecover(rejection)) {
	 *           return responseOrNewPromise
	 *         }
	 *         return $q.reject(rejection);
	 *       };
	 *     }
	 *   });
	 *
	 *   $httpProvider.interceptors.push('myHttpInterceptor');
	 *
	 *
	 *   // register the interceptor via an anonymous factory
	 *   $httpProvider.interceptors.push(function(dependency1, dependency2) {
	 *     return {
	 *      'request': function(config, $q) {
	 *          // same as above
	 *       },
	 *       'response': function(response, $q) {
	 *          // same as above
	 *       }
	 *   });
	 * </pre>
	 * # Security Considerations
	 *
	 * When designing web applications, consider security threats from:
	 *
	 * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
	 *   JSON vulnerability}
	 * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
	 *
	 * Both server and the client must cooperate in order to eliminate these threats. VX comes
	 * pre-configured with strategies that address these issues, but for this to work backend server
	 * cooperation is required.
	 *
	 * ## JSON Vulnerability Protection
	 *
	 * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
	 * JSON vulnerability} allows third party website to turn your JSON resource URL into
	 * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
	 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
	 * VX will automatically strip the prefix before processing it as JSON.
	 *
	 * For example if your server needs to return:
	 * <pre>
	 * ['one','two']
	 * </pre>
	 *
	 * which is vulnerable to attack, your server can return:
	 * <pre>
	 * )]}',
	 * ['one','two']
	 * </pre>
	 *
	 * VX will strip the prefix, before processing the JSON.
	 *
	 *
	 * ## Cross Site Request Forgery (XSRF) Protection
	 *
	 * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
	 * an unauthorized site can gain your user's private data. VX provides a mechanism
	 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
	 * (by default `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only JavaScript that
	 * runs on your domain could read the cookie, your server can be assured that the XHR came from
	 * JavaScript running on your domain. The header will not be set for cross-domain requests.
	 *
	 * To take advantage of this, your server needs to set a token in a JavaScript readable session
	 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
	 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
	 * that only JavaScript running on your domain could have sent the request. The token must be
	 * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
	 * up its own tokens). We recommend that the token is a digest of your site's authentication
	 * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
	 *
	 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
	 * properties of either $httpProvider.defaults, or the per-request config object.
	 *
	 * @param {object} config Object describing the request to be made and how it should be
	 *    processed. The object has following properties:
	 *
	 *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
	 *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
	 *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned to
	 *      `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
	 *    - **data** – `{string|Object}` – Data to be sent as the request message data.
	 *    - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server.
	 *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
	 *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
	 *    - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
	 *      transform function or an array of such functions. The transform function takes the http
	 *      request body and headers and returns its transformed (typically serialized) version.
	 *    - **transformResponse** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
	 *      transform function or an array of such functions. The transform function takes the http
	 *      response body and headers and returns its transformed (typically deserialized) version.
	 *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
	 *      GET request, otherwise if a cache instance built with
	 *      {@link vx.$cacheFactory $cacheFactory}, this cache will be used for
	 *      caching.
	 *    - **timeout** – `{number|Promise}` – timeout in milliseconds.
	 *    - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
	 *      XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
	 *      requests with credentials} for more information.
	 *    - **responseType** - `{string}` - see {@link
	 *      https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
	 *
	 * @returns {HttpPromise} Returns a {@link vx.$q promise} object with the
	 *   standard `then` method and two http specific methods: `success` and `error`. The `then`
	 *   method takes two arguments a success and an error callback which will be called with a
	 *   response object. The `success` and `error` methods take a single argument - a function that
	 *   will be called when the request succeeds or fails respectively. The arguments passed into
	 *   these functions are destructured representation of the response object passed into the
	 *   `then` method. The response object has these properties:
	 *
	 *   - **data** – `{string|Object}` – The response body transformed with the transform functions.
	 *   - **status** – `{number}` – HTTP status code of the response.
	 *   - **headers** – `{function([headerName])}` – Header getter function.
	 *   - **config** – `{Object}` – The configuration object that was used to generate the request.
	 *
	 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
	 *   requests. This is primarily meant to be used for debugging purposes.
	 *
	 *
	 * @example
	 <example>
	 <file name="index.html">
	 <div v-controller="FetchCtrl">
	 <select v-model="method">
	 <option>GET</option>
	 <option>JSONP</option>
	 </select>
	 <input type="text" v-model="url" size="80"/>
	 <button v-click="fetch()">fetch</button><br>
	 <button v-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
	 <button v-click="updateModel('JSONP', 'http://vxjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">Sample JSONP</button>
	 <button v-click="updateModel('JSONP', 'http://vxjs.org/doesntexist&callback=JSON_CALLBACK')">Invalid JSONP</button>
	 <pre>http status code: {{status}}</pre>
	 <pre>http response data: {{data}}</pre>
	 </div>
	 </file>
	 <file name="script.js">
	 function FetchCtrl($scope, $http, $templateCache) {
	 $scope.method = 'GET';
	 $scope.url = 'http-hello.html';

	 $scope.fetch = function() {
	 $scope.code = null;
	 $scope.response = null;

	 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
	 success(function(data, status) {
	 $scope.status = status;
	 $scope.data = data;
	 }).
	 error(function(data, status) {
	 $scope.data = data || "Request failed";
	 $scope.status = status;
	 });
	 };

	 $scope.updateModel = function(method, url) {
	 $scope.method = method;
	 $scope.url = url;
	 };
	 }
	 </file>
	 <file name="http-hello.html">
	 Hello, $http!
	 </file>
	 <file name="scenario.js">
	 it('should make an xhr GET request', function() {
	 element(':button:contains("Sample GET")').click();
	 element(':button:contains("fetch")').click();
	 expect(binding('status')).toBe('200');
	 expect(binding('data')).toMatch(/Hello, \$http!/);
	 });

	 it('should make a JSONP request to vxjs.org', function() {
	 element(':button:contains("Sample JSONP")').click();
	 element(':button:contains("fetch")').click();
	 expect(binding('status')).toBe('200');
	 expect(binding('data')).toMatch(/Super Hero!/);
	 });

	 it('should make JSONP request to invalid URL and invoke the error handler',
	 function() {
	 element(':button:contains("Invalid JSONP")').click();
	 element(':button:contains("fetch")').click();
	 expect(binding('status')).toBe('0');
	 expect(binding('data')).toBe('Request failed');
	 });
	 </file>
	 </example>
	 */
	function $HttpProvider() {
		var JSON_START = /^\s*(\[|\{[^\{])/, JSON_END = /[\}\]]\s*$/, PROTECTION_PREFIX = /^\)\]\}',?\n/, //
		CONTENT_TYPE_APPLICATION_JSON = {
			'Content-Type' : 'application/json.json;charset=utf-8'
		};

		var defaults = this.defaults = {
			// transform incoming response data
			transformResponse : [
			function(data) {
				if (isString(data)) {
					data = data.replace(PROTECTION_PREFIX, '');
					if (JSON_START.test(data) && JSON_END.test(data))
						data = fromJson(data, true);
				}
				return data;
			}],

			// transform outgoing request data
			transformRequest : [
			function(d) {
				return jsonRequest && isObject(d) && !isFile(d) ? toJson(d) : d;
			}],

			// default headers
			headers : {
				common : {
					'Accept' : 'application/json.json, text/plain, */*'
				},
				post : CONTENT_TYPE_APPLICATION_JSON,
				put : CONTENT_TYPE_APPLICATION_JSON,
				patch : CONTENT_TYPE_APPLICATION_JSON
			},

			xsrfCookieName : 'XSRF-TOKEN',
			xsrfHeaderName : 'X-XSRF-TOKEN'
		};

		// responseInterceptors already removed
		//var providerResponseInterceptors = this.responseInterceptors = [];
		/**
		 * Are order by request. I.E. they are applied in the same order as
		 * array on request, but revers order on response.
		 */
		var interceptorFactories = this.interceptors = [];

		var cacheDisabled = true, jsonRequest = true, defaultFn;
		this.useCache = function(cache) {
			cacheDisabled = !cache;
		};

		/* for old application, prefer using $.param as request, rather than use json.json*/
		this.useJsonRequest = function(json) {
			jsonRequest = !!json;
		};

		this.setDefaultCallback = function(fn) {
			defaultFn = fn;
		};

		this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', httpFactory];

		function httpFactory($httpBackend, $browser, $cacheFactory, $rootScope, $$q, $injector) {
			var defaultCache = $cacheFactory('$http');
			/**
			 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
			 * The reversal is needed so that we can build up the interception chain around the
			 * server request.
			 */
			var reversedInterceptors = [];

			forEach(interceptorFactories, function(interceptorFactory) {
				reversedInterceptors.unshift(isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
			});

			function $http(requestConfig) {
				/*
				 config: {
				 method:
				 headers:
				 url:
				 data:
				 transformRequest:
				 transformResponse:
				 cache:
				 }
				 */
				var config = {
					transformRequest : defaults.transformRequest,
					transformResponse : defaults.transformResponse
				};
				var headers = {};

				extend(config, requestConfig);
				config.headers = headers;
				config.method = uppercase(config.method);

				extend(headers, defaults.headers.common, defaults.headers[lowercase(config.method)], requestConfig.headers);

				var xsrfValue = isSameDomain(config.url, $browser.url()) ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] : undefined;
				if (xsrfValue) {
					headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
				}

				headers.$Referer = config.$referer || '/';
				var scope = config.$scope || $rootScope, $q = $$q.$new(scope);
				var serverRequest = function(config) {
					var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
					
					// strip content-type if data is undefined
					if (isUndefined(config.data) || !jsonRequest) {
						delete headers['Content-Type'];
					}

					if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
						config.withCredentials = defaults.withCredentials;
					}

					// send request
					return sendReq(config, reqData, headers, $q).then(transformResponse, transformResponse);
				};

				var chain = [serverRequest, undefined];

				// apply interceptors
				forEach(reversedInterceptors, function(interceptor) {
					if (interceptor.request || interceptor.requestError) {
						chain.unshift(interceptor.request, interceptor.requestError);
					}
					if (interceptor.response || interceptor.responseError) {
						chain.push(interceptor.response, interceptor.responseError);
					}
				});

				var needApply = true, promise;
				if (serverRequest !== chain[0])
					promise = $q.when(config);
				else {
					needApply = false;
					promise = serverRequest(config);
					chain.shift();
					chain.shift();
				}

				while (chain.length) {
					var thenFn = chain.shift();
					var rejectFn = chain.shift();
					promise = promise.then(thenFn, rejectFn);
				}

				var processed;

				promise.success = function(fn) {
					promise.then(function(response) {
						if (!processed)
							fn(response.data, response.status, response.headers, config);
					});
					return promise;
				};

				promise.error = function(fn) {
					promise.then(null, function(response) {
						if (!processed)
							fn(response.data, response.status, response.headers, config);
					});
					return promise;
				};

				if (defaultFn) {
					promise.then(function(response) {
						processed = defaultFn(response.data, response.status, response.headers, config);
					}, function(response) {
						processed = defaultFn(response.data, response.status, response.headers, config);
					});
				}

				if (needApply && !scope.$$phase && (!(scope.$$applied || scope.$root).$$phase))
					scope.$apply(null, '$httpRequestInterceptors->' + config.url);

				return promise;

				function transformResponse(response) {
					// make a copy since the response must be cacheable
					var resp = extend({}, response, {
						data : transformData(response.data, response.headers, config.transformResponse)
					});
					return (isSuccess(response.status)) ? resp : $q.reject(resp);
				}

			}


			$http.pendingRequests = [];

			/**
			 * @vxdoc method
			 * @name vx.$http#get
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `GET` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */

			/**
			 * @vxdoc method
			 * @name vx.$http#delete
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `DELETE` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */

			/**
			 * @vxdoc method
			 * @name vx.$http#head
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `HEAD` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */

			/**
			 * @vxdoc method
			 * @name vx.$http#jsonp
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `JSONP` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request.
			 *                     Should contain `JSON_CALLBACK` string.
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */
			createShortMethods('get', 'delete', 'head', 'jsonp');

			/**
			 * @vxdoc method
			 * @name vx.$http#post
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `POST` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request
			 * @param {*} data Request content
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */

			/**
			 * @vxdoc method
			 * @name vx.$http#put
			 * @methodOf vx.$http
			 *
			 * @description
			 * Shortcut method to perform `PUT` request
			 *
			 * @param {string} url Relative or absolute URL specifying the destination of the request
			 * @param {*} data Request content
			 * @param {Object=} config Optional configuration object
			 * @returns {HttpPromise} Future object
			 */
			createShortMethodsWithData('post', 'put');

			/**
			 * @vxdoc property
			 * @name vx.$http#defaults
			 * @propertyOf vx.$http
			 *
			 * @description
			 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
			 * default headers, withCredentials as well as request and response transformations.
			 *
			 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
			 */
			$http.defaults = defaults;

			return $http;

			// -------------------------------------------------

			function createShortMethods(names) {
				forEach(arguments, function(name) {
					$http[name] = function(url, config) {
						return $http(extend(config || {}, {
							method : name,
							url : url
						}));
					};
				});
			}

			function createShortMethodsWithData(name) {
				forEach(arguments, function(name) {
					$http[name] = function(url, data, config) {
						return $http(extend(config || {}, {
							method : name,
							url : url,
							data : data
						}));
					};
				});
			}

			/**
			 * Makes the request
			 *
			 * !!! ACCESSES CLOSURE VARS:
			 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
			 */
			function sendReq(config, reqData, reqHeaders, $q) {
				var deferred = $q.defer(), promise = deferred.promise, cache, cachedResp, url = buildUrl(config.url, config.params);

				$http.pendingRequests.push(config);
				promise.then(removePendingReq, removePendingReq);

				if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
					cache = isObject(config.cache) ? config.cache : isObject(defaults.cache) ? defaults.cache : defaultCache;
				}

				if (cache) {
					cachedResp = cache.get(url);
					if (cachedResp) {
						if (cachedResp.then) {
							// cached request has already been sent, but there is no response yet
							cachedResp.then(removePendingReq, removePendingReq);
							return cachedResp;
						} else {
							// serving from cache
							if (isArray(cachedResp)) {
								resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]));
							} else {
								resolvePromise(cachedResp, 200, {});
							}
						}
					} else if (!cacheDisabled) {
						// put the promise for the non-transformed response into cache as a placeholder
						cache.put(url, promise);
					}
				}

				// if we won't have the response in cache, send the request to the backend
				if (!cachedResp) {
					var ajaxPort = config.ajaxPort || config.$scope && config.$scope.$$ajaxPort;
					$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, config.withCredentials, config.responseType, ajaxPort);
				}

				return promise;

				// --------------------------------------------------------
				/**
				 * Callback registered to $httpBackend():
				 *  - caches the response if desired
				 *  - resolves the raw $http promise
				 *  - calls $apply
				 */
				function done(status, response, headersString) {
					if (!cacheDisabled && cache) {
						if (isSuccess(status)) {
							cache.put(url, [status, response, parseHeaders(headersString)]);
						} else {
							// remove promise from the cache
							cache.remove(url);
						}
					}
					var scope = config.$scope || $rootScope;
					resolvePromise(response, status, headersString);
					if (!scope.$$phase && (!(scope.$$applied || scope.$root).$$phase))
						scope.$apply(null, '$http->' + url);
				}

				/**
				 * Resolves the raw $http promise.
				 */
				function resolvePromise(response, status, headers) {
					// normalize internal statuses to 0
					status = Math.max(status, 0);
					//
					(isSuccess(status) ? deferred.resolve : deferred.reject)({
						data : response,
						status : status,
						headers : headersGetter(headers),
						config : config
					});
				}

				function removePendingReq() {
					var idx = indexOf($http.pendingRequests, config);
					if (idx !== -1)
						$http.pendingRequests.splice(idx, 1);
				}

				function buildUrl(url, params) {
					if (!params)
						return url;
					var parts = [];
					forEachSorted(params, function(value, key) {
						if (value === null || value === undefined)
							return;
						if (!isArray(value))
							value = [value];

						forEach(value, function(v) {
							if (isObject(v)) {
								v = toJson(v);
							}
							parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
						});
					});
					return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
				}

			}

		}

		/**
		 * Parse headers into key value object
		 *
		 * @param {string} headers Raw headers as a string
		 * @returns {Object} Parsed headers as key value object
		 */
		function parseHeaders(headers) {
			var parsed = {}, key, val, i;
			if (!headers)
				return parsed;
			forEach(headers.split('\n'), function(line) {
				i = line.indexOf(':');
				key = lowercase(trim(line.substr(0, i)));
				val = trim(line.substr(i + 1));
				if (key) {
					if (parsed[key]) {
						parsed[key] += ', ' + val;
					} else {
						parsed[key] = val;
					}
				}
			});
			return parsed;
		}

		var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.\-]*)?(:([0-9]+))?(.*)$/;

		/**
		 * Parse a request and location URL and determine whether this is a same-domain request.
		 *
		 * @param {string} requestUrl The url of the request.
		 * @param {string} locationUrl The current browser location url.
		 * @returns {boolean} Whether the request is for the same domain.
		 */
		function isSameDomain(requestUrl, locationUrl) {
			var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl);
			// if requestUrl is relative, the regex does not match.
			if (match == null)
				return true;

			var domain1 = {
				protocol : match[2],
				host : match[4],
				port : int(match[6]) || DEFAULT_PORTS[match[2]] || null,
				// IE8 sets unmatched groups to '' instead of undefined.
				relativeProtocol : match[2] === undefined || match[2] === ''
			};

			match = URL_MATCH.exec(locationUrl);
			var domain2 = {
				protocol : match[1],
				host : match[3],
				port : int(match[5]) || DEFAULT_PORTS[match[1]] || null
			};

			return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) && //
			domain1.host == domain2.host && //
			(domain1.port == domain2.port || (domain1.relativeProtocol && domain2.port == DEFAULT_PORTS[domain2.protocol]));
		}

		/**
		 * Returns a function that provides access to parsed headers.
		 *
		 * Headers are lazy parsed when first requested.
		 * @see parseHeaders
		 *
		 * @param {(string|Object)} headers Headers to provide access to.
		 * @returns {function(string=)} Returns a getter function which if called with:
		 *
		 *   - if called with single an argument returns a single header value or null
		 *   - if called with no arguments returns an object containing all headers.
		 */
		function headersGetter(headers) {
			var headersObj = isObject(headers) ? headers : undefined;
			var fn = function(name) {
				if (!headersObj)
					headersObj = parseHeaders(headers);
				if (name) {
					return headersObj[lowercase(name)] || null;
				}
				return headersObj;
			};
			fn.headers = headers;
			return fn;
		}

		/**
		 * Chain all given functions
		 *
		 * This function is used for both request and response transforming
		 *
		 * @param {*} data Data to transform.
		 * @param {function(string=)} headers Http headers getter fn.
		 * @param {(function|Array.<function>)} fns Function or an array of functions.
		 * @returns {*} Transformed data.
		 */
		function transformData(data, headers, fns) {
			if (isFunction(fns))
				return fns(data, headers);
			forEach(fns, function(fn) {
				data = fn(data, headers);
			});
			return data;
		}

		function isSuccess(status) {
			return 200 <= status && status < 300;
		}

	}

	////////////////////////////////
	// $QProvider
	///////////////////////////////

	/**
	 * @vxdoc service
	 * @name vx.$q
	 * @requires $rootScope
	 *
	 * @description
	 * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
	 *
	 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
	 * interface for interacting with an object that represents the result of an action that is
	 * performed asynchronously, and may or may not be finished at any given point in time.
	 *
	 * From the perspective of dealing with error handling, deferred and promise APIs are to
	 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
	 *
	 * <pre>
	 *   // for the purpose of this example let's assume that variables `$q` and `scope` are
	 *   // available in the current lexical scope (they could have been injected or passed in).
	 *
	 *   function asyncGreet(name) {
	 *     var deferred = $q.defer();
	 *
	 *     setTimeout(function() {
	 *       // since this fn executes async in a future turn of the event loop, we need to wrap
	 *       // our code into an $apply call so that the model changes are properly observed.
	 *       scope.$apply(function() {
	 *         if (okToGreet(name)) {
	 *           deferred.resolve('Hello, ' + name + '!');
	 *         } else {
	 *           deferred.reject('Greeting ' + name + ' is not allowed.');
	 *         }
	 *       });
	 *     }, 1000);
	 *
	 *     return deferred.promise;
	 *   }
	 *
	 *   var promise = asyncGreet('Robin Hood');
	 *   promise.then(function(greeting, $q) {
	 *     alert('Success: ' + greeting);
	 *   }, function(reason, $q) {
	 *     alert('Failed: ' + reason);
	 *   });
	 * </pre>
	 *
	 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
	 * comes in the way of
	 * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
	 *
	 * Additionally the promise api allows for composition that is very hard to do with the
	 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
	 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
	 * section on serial or parallel joining of promises.
	 *
	 *
	 * # The Deferred API
	 *
	 * A new instance of deferred is constructed by calling `$q.defer()`.
	 *
	 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
	 * that can be used for signaling the successful or unsuccessful completion of the task.
	 *
	 * **Methods**
	 *
	 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
	 *   constructed via `$q.reject`, the promise will be rejected instead.
	 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
	 *   resolving it with a rejection constructed via `$q.reject`.
	 *
	 * **Properties**
	 *
	 * - promise – `{Promise}` – promise object associated with this deferred.
	 *
	 *
	 * # The Promise API
	 *
	 * A new promise instance is created when a deferred instance is created and can be retrieved by
	 * calling `deferred.promise`.
	 *
	 * The purpose of the promise object is to allow for interested parties to get access to the result
	 * of the deferred task when it completes.
	 *
	 * **Methods**
	 *
	 * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved
	 *   or rejected calls one of the success or error callbacks asynchronously as soon as the result
	 *   is available. The callbacks are called with a single argument the result or rejection reason.
	 *
	 *   This method *returns a new promise* which is resolved or rejected via the return value of the
	 *   `successCallback` or `errorCallback`.
	 *
	 * - `always(callback)` – allows you to observe either the fulfillment or rejection of a promise,
	 *   but to do so without modifying the final value. This is useful to release resources or do some
	 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
	 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
	 *   more information.
	 *
	 * # Chaining promises
	 *
	 * Because calling `then` api of a promise returns a new derived promise, it is easily possible
	 * to create a chain of promises:
	 *
	 * <pre>
	 *   promiseB = promiseA.then(function(result, $q) {
	 *     return result + 1;
	 *   });
	 *
	 *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
	 *   // the result of promiseA incremented by 1
	 * </pre>
	 *
	 * It is possible to create chains of any length and since a promise can be resolved with another
	 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
	 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
	 * $http's response interceptors.
	 *
	 *
	 * # Differences between Kris Kowal's Q and $q
	 *
	 *  There are three main differences:
	 *
	 * - $q is integrated with the {@link vx.$rootScope.Scope} Scope model observation
	 *   mechanism in vx, which means faster propagation of resolution or rejection into your
	 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
	 * - $q promises are recognized by the templating engine in vx, which means that in templates
	 *   you can treat promises attached to a scope as if they were the resulting values.
	 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
	 *   all the important functionality needed for common async tasks.
	 *
	 *  # Testing
	 *
	 *  <pre>
	 *    it('should simulate promise', inject(function($q, $rootScope) {
	 *      var deferred = $q.defer();
	 *      var promise = deferred.promise;
	 *      var resolvedValue;
	 *
	 *      promise.then(function(value, $q) { resolvedValue = value; });
	 *      expect(resolvedValue).toBeUndefined();
	 *
	 *      // Simulate resolving of promise
	 *      deferred.resolve(123);
	 *      // Note that the 'then' function does not get called synchronously.
	 *      // This is because we want the promise API to always be async, whether or not
	 *      // it got called synchronously or asynchronously.
	 *      expect(resolvedValue).toBeUndefined();
	 *
	 *      // Propagate promise resolution to 'then' functions using $apply().
	 *      $rootScope.$apply();
	 *      expect(resolvedValue).toEqual(123);
	 *    });
	 *  </pre>
	 */
	function $QProvider() {

		/**
		 * Constructs a promise manager.
		 *
		 * @param {function(function)} nextTick Function for executing functions in the next turn.
		 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
		 *     debugging purposes.
		 * @returns {object} Promise manager.
		 */
		function qFactory(nextTick/*,                                                                                                                                                                                                                                                            exceptionHandler*/, log) {
			/**
			 * @vxdoc
			 * @name vx.$q#defer
			 * @methodOf vx.$q
			 * @description
			 * Creates a `Deferred` object which represents a task which will finish in the future.
			 *
			 * @returns {Deferred} Returns a new instance of deferred.
			 */
			var defer = function() {
				var pending = [], value, deferred;
				deferred = {
					resolve : function(val) {
						if (pending) {
							var callbacks = pending;
							pending = undefined;
							value = ref(val);
							if (callbacks.length) {
								nextTick(function() {
									var callback;
									for (var i = 0, ii = callbacks.length; i < ii; i++) {
										callback = callbacks[i];
										value.then(callback[0], callback[1]);
									}
								});
							}
						}
					},
					reject : function(reason) {
						deferred.resolve(reject(reason));
					},
					promise : {
						then : function(callback, errback) {
							var result = defer();
							var wrappedCallback = function(value) {
								//try {
								result.resolve((callback || defaultCallback)(value, $q));
								//} catch(e) {
								//  log.error(e);
								//	exceptionHandler(e);
								//	result.reject(e);
								//}
							};
							var wrappedErrback = function(reason) {
								//try {
								result.resolve((errback || defaultErrback)(reason, $q));
								//} catch(e) {
								//  log.error(e);
								//	exceptionHandler(e);
								//	result.reject(e);
								//}
							};
							if (pending) {
								pending.push([wrappedCallback, wrappedErrback]);
							} else {
								value.then(wrappedCallback, wrappedErrback);
							}
							return result.promise;
						},
						always : function(callback) {

							function makePromise(value, resolved) {
								var result = defer();
								if (resolved) {
									result.resolve(value);
								} else {
									result.reject(value);
								}
								return result.promise;
							}

							function handleCallback(value, isResolved) {
								var callbackOutput = null;
								try {
									callbackOutput = (callback || defaultCallback)();
								} catch(e) {
									return makePromise(e, false);
								}
								if (callbackOutput && callbackOutput.then) {
									return callbackOutput.then(function() {
										return makePromise(value, isResolved);
									}, function(error) {
										return makePromise(error, false);
									});
								} else {
									return makePromise(value, isResolved);
								}
							}

							return this.then(function(value) {
								return handleCallback(value, true);
							}, function(error) {
								return handleCallback(error, false);
							});
						}
					}
				};
				return deferred;
			};
			var ref = function(value) {
				if (value && value.then)
					return value;
				return {
					then : function(callback) {
						var result = defer();
						nextTick(function() {
							result.resolve(callback(value, $q));
						});
						return result.promise;
					}
				};
			};
			/**
			 * @vxdoc
			 * @name vx.$q#reject
			 * @methodOf vx.$q
			 * @description
			 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
			 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
			 * a promise chain, you don't need to worry about it.
			 *
			 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
			 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
			 * a promise error callback and you want to forward the error to the promise derived from the
			 * current promise, you have to "rethrow" the error by returning a rejection constructed via
			 * `reject`.
			 *
			 * <pre>
			 *   promiseB = promiseA.then(function(result, $q) {
			 *     // success: do something and resolve promiseB
			 *     //          with the old or a new result
			 *     return result;
			 *   }, function(reason, $q) {
			 *     // error: handle the error if possible and
			 *     //        resolve promiseB with newPromiseOrValue,
			 *     //        otherwise forward the rejection to promiseB
			 *     if (canHandle(reason)) {
			 *      // handle the error and recover
			 *      return newPromiseOrValue;
			 *     }
			 *     return $q.reject(reason);
			 *   });
			 * </pre>
			 *
			 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
			 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
			 */
			var reject = function(reason) {
				return {
					then : function(callback, errback) {
						var result = defer();
						nextTick(function() {
							result.resolve((errback || defaultErrback)(reason, $q));
						});
						return result.promise;
					}
				};
			};
			/**
			 * @vxdoc
			 * @name vx.$q#when
			 * @methodOf vx.$q
			 * @description
			 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
			 * This is useful when you are dealing with an object that might or might not be a promise, or if
			 * the promise comes from a source that can't be trusted.
			 *
			 * @param {*} value Value or a promise
			 * @returns {Promise} Returns a promise of the passed value or promise
			 */
			var when = function(value, callback, errback) {
				var result = defer(), done;
				var wrappedCallback = function(value) {
					//try {
					return (callback || defaultCallback)(value, $q);
					//} catch (e) {
					//  log.error(e);
					//	exceptionHandler(e);
					//	return reject(e);
					//}
				};
				var wrappedErrback = function(reason) {
					//try {
					return (errback || defaultErrback)(reason, $q);
					//} catch (e) {
					//  log.error(e);
					//	exceptionHandler(e);
					//	return reject(e);
					//}
				};
				nextTick(function() {
					ref(value).then(function(value) {
						if (done)
							return;
						done = true;
						result.resolve(ref(value).then(wrappedCallback, wrappedErrback));
					}, function(reason) {
						if (done)
							return;
						done = true;
						result.resolve(wrappedErrback(reason));
					});
				});
				return result.promise;
			};

			function defaultCallback(value) {
				return value;
			}

			function defaultErrback(reason) {
				return reject(reason);
			}

			/**
			 * @vxdoc
			 * @name vx.$q#all
			 * @methodOf vx.$q
			 * @description
			 * Combines multiple promises into a single promise that is resolved when all of the input
			 * promises are resolved.
			 *
			 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
			 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
			 *   each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of
			 *   the promises is resolved with a rejection, this resulting promise will be resolved with the
			 *   same rejection.
			 */
			function all(promises) {
				var deferred = defer(), counter = 0, results = isArray(promises) ? [] : {};
				forEach(promises, function(promise, key) {
					counter++;
					ref(promise).then(function(value) {
						if (results.hasOwnProperty(key))
							return;
						results[key] = value;
						if (!(--counter))
							deferred.resolve(results);
					}, function(reason) {
						if (results.hasOwnProperty(key))
							return;
						deferred.reject(reason);
					});
				});
				if (counter === 0) {
					deferred.resolve(results);
				}
				return deferred.promise;
			}

			var $q = {
				defer : defer,
				reject : reject,
				when : when,
				all : all
			};

			return $q;
		}


		this.$get = ['$rootScope', /*'$exceptionHandler',*/'$log',
		function($rootScope, /*$exceptionHandler,*/$log) {
			var $q = qFactory(function(callback) {
				$rootScope.$evalAsync(callback);
			}, /*$exceptionHandler,*/$log);

			$q.$new = function(scope) {
				if (scope === $rootScope)
					return $q;
				return qFactory(function(callback) {
					scope.$evalAsync(callback);
				}, /*$exceptionHandler,*/$log);
			};

			return $q;
		}];

	}

	////////////////////////////////
	// $HttpBackendProvider
	///////////////////////////////

	/**
	 * @vxdoc object
	 * @name vx.$httpBackend
	 * @requires $browser
	 * @requires $window
	 * @requires $document
	 *
	 * @description
	 * HTTP backend used by the {@link vx.$http service} that delegates to
	 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
	 *
	 * You should never need to use this service directly, instead use the higher-level abstractions:
	 * {@link vx.$http $http} or {@link vResource.$resource $resource}.
	 *
	 * During testing this implementation is swapped with {@link vMock.$httpBackend mock
	 * $httpBackend} which can be trained with responses.
	 */

	function $HttpBackendProvider() {
		var ajaxTransport, antiCache = 0/* 0-disable, 1-every load time, 2-every request time */;
		var setting = {
			ajaxQueue : null,
			ajaxTimeout : 30000/* 30 seconds */,
			ajaxQueueSize : 5,
			ajaxAborted : false, // false-no, true-yes
			beforeSend : noop,
			afterReceived : noop,
			$requestCount : 0
		}, ports = {};

		this.useExternalAjax = function(yes) {
			if (!!yes)
				ajaxTransport = jqLite.ajax;
		};

		//0-disable, 1-every load time, 2-every request time
		this.useAntiCache = function(anti) {
			antiCache = anti;
		};

		//---- START of default setting config
		this.indicator = function(pre, post) {
			setting.beforeSend = pre;
			setting.afterReceived = post;
		};

		this.useAjaxQueue = function(queue, aborted) {
			setting.ajaxQueueSize = isNumber(queue) ? queue : !!queue ? 5 : 0;
			setting.ajaxAborted = !!aborted;
		};

		this.setTimeout = function(timeout) {
			setting.ajaxTimeout = timeout;
		};
		//---- END of default setting config

		/**
		 * could setting:
		 *   ajaxTimeout : 30000, // in milliseconds
		 *   ajaxQueueSize : 5,
		 *   ajaxAborted : false, // false-no, true-yes
		 *   beforeSend : noop,
		 *   afterReceived : noop,
		 */
		this.config = function(port, obj) {
			if (arguments.length === 1) {
				obj = port;
				extend(setting, obj);
			} else {
				ports[port] = obj;
			}
		};

		function ajaxQueueFactory(log, config) {
			return {
				reqs : [],
				// current status
				xhr : false,
				requesting : false,
				// function
				next : function() {
					if (this.reqs.length === 0 || this.requesting)
						return;
					var req = this.reqs.shift(), complete = req.complete, self = this;
					req.complete = function() {
						if (complete)
							complete.apply(this, arguments);
						self.requesting = false, self.xhr = false;
						self.next();
					};
					this.requesting = req, this.xhr = ajaxTransport(req);
				},
				add : function(req) {
					if (config.ajaxQueueSize === 0) {
						ajaxTransport(req);
						return;
					}

					//abort same url
					if (this.requesting && requestEqual(this.requesting, req)) {
						if (this.xhr.readyState !== 4) {
							log.warn("httpBackend duplicate sending: " + req.url);
							if (config.ajaxAborted) {
								this.xhr.abort();
								this.requesting = false, this.xhr = false;
							} else {
								return requestAppend(this.requesting, req);
							}
						}
					}

					// max queue size, remove first one
					if (this.reqs.length === config.ajaxQueueSize) {
						var p = this.reqs.shift();
						log.warn("httpBackend queue overflow, add: " + req.url + ", remove: " + p.url);
					}

					this.reqs.push(req);
					this.next();
				}
			};

			function requestEqual(req, req1) {
				return req.url === req1.url && req.type === req1.type && equals(req.data, req1.data);
			}

			function requestAppend(req, req1) {
				var reqSuccess = req.success, reqError = req.error;
				req.success = function(data, status, xhr) {
					reqSuccess(data, status, xhr);
					req1.success(data, status, xhr);
				};
				req.error = function(xhr, status, error) {
					reqError(xhr, status, error);
					req1.error(xhr, status, error);
				};
			}

		}


		this.$get = ['$browser', '$window', '$document', '$log',
		function($browser, $window, $document, $log) {
			return createHttpBackend($browser, $window.vx.callbacks, $window.location.protocol.replace(':', ''), $log);
		}];

		//@formatter:off
		var XHR = window.XMLHttpRequest || function() {
			try { return new window.ActiveXObject("Microsoft.XMLHTTP"); } catch (e0) {}
			try { return new window.ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
			try { return new window.ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
			try { return new window.ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {	}
			throw vError(52, "This browser does not support XMLHttpRequest.");
		};
		//@formatter:on

		function createHttpBackend($browser, callbacks, locationProtocol, log) {
			if (!ajaxTransport)
				ajaxTransport = ajaxReq;

			return httpBackend;

			// TODO(vojta): fix the signature
			// for script signature is httpBackend(node, url, type, callback, ajaxPort)
			function httpBackend(method, url, post, callback, headers, timeout, withCredentials, responseType, ajaxPort) {

				// for load script
				var scripted = false;
				if (arguments.length < 9) {
					ajaxPort = arguments[4];
					scripted = true;
				}
				
				// initiate ajaxQueue for ajaxPort config
				var config = setting;
				if (isDefined(ajaxPort)) {
					config = ports[ajaxPort];
					if (!config || !config.ajaxQueue) {
						config = ports[ajaxPort] = extend({}, setting, config || {});
						config.$requestCount = 0;
						config.ajaxQueue = ajaxQueueFactory(log, config);
					}
				} else if (!config.ajaxQueue) {
					config.ajaxQueue = ajaxQueueFactory(log, config);
				}

				if (config.$requestCount === 0)
					config.beforeSend();
				config.$requestCount++;

				url = url || $browser.url();
				// Add anti-cache in url if needed
				if (scripted || method === 'GET') {
					url = antiCacheURL(url);
				}

				$browser.$$incOutstandingRequestCount();
				
				if (scripted) {
					var node = method, type = post;
					scriptReq(url, function(status) {
						if (!status) {
							completeRequest(callback, 200, true);
						} else {
							completeRequest(callback, status);
						}
					}, node, type, timeout || config.ajaxTimeout);
				} else if (method == 'JSONP') {
					/*
					* Sample:
					* request : http://xxx.com/xxx.do?callback=JSON_CALLBACK&...
					*   *JSON_CALLBACK will replace with callbackId (_xxx)
					* server-side should return
					* vx.callbacks._xxx (object);  // object is any js object
					*/
					//***  jQuery jsonp cannot deal with error
					var callbackId = '_' + (callbacks.counter++).toString(36);
					callbacks[callbackId] = function(data) {
						callbacks[callbackId].data = data;
					};
					scriptReq(url.replace('JSON_CALLBACK', 'vx.callbacks.' + callbackId), function(status) {
						if (callbacks[callbackId].data) {
							completeRequest(callback, 200, callbacks[callbackId].data);
						} else {
							completeRequest(callback, status);
						}
						delete callbacks[callbackId];
					}, null, null, timeout || config.ajaxTimeout);
				} else {
					var ajaxSetting = {
						url : url,
						type : method,
						dataType : 'text',
						data : post,
						headers : headers,
						async : true,
						timeout : timeout || config.ajaxTimeout,
						converters : {//*** only for jQuery
							"text json" : true //disable jQuery json.json parse
						},
						// responseText is the old-school way of retrieving response (supported by IE8 & 9)
						// response and responseType properties were introduced in XHR Level2 spec (supported by IE10)
						success : function(data, status, xhr) {
							completeRequest(callback, xhr.status, (xhr.responseType ? xhr.response : xhr.responseText) || data/* jsonp */, getAllResponseHeaders(xhr));
						},
						error : function(xhr, status, error) {
							completeRequest(callback, status || xhr.status, xhr.statusText, getAllResponseHeaders(xhr));
						}
					};
					ajaxSetting.xhrFields = {};
					//*** only for jQuery
					if (withCredentials) {
						ajaxSetting.xhrFields.withCredentials = true;
					}
					if (responseType) {
						ajaxSetting.xhrFields.responseType = responseType;
					}

					var ajaxQ = config.ajaxQueue;
					ajaxQ.add(ajaxSetting);

				}

				function getAllResponseHeaders(xhr) {
					var responseHeaders;
					try {
						responseHeaders = xhr.getAllResponseHeaders();
					} catch(e) {
					}

					// TODO(vojta): remove once Firefox 21 gets released.
					// begin: workaround to overcome Firefox CORS http response headers bug
					// https://bugzilla.mozilla.org/show_bug.cgi?id=608735
					// Firefox already patched in nightly. Should land in Firefox 21.

					// CORS "simple response headers" http://www.w3.org/TR/cors/
					var value, simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma"];
					if (!responseHeaders) {
						responseHeaders = "";
						forEach(simpleHeaders, function(header) {
							var value = xhr.getResponseHeader(header);
							if (value) {
								responseHeaders += header + ": " + value + "\n";
							}
						});
					}
					// end of the workaround.
					return responseHeaders;
				}

				function completeRequest(callback, status, response, headersString) {
					var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];

					// fix status code for file protocol (it's always 0)
					status = (protocol == 'file') ? ( response ? 200 : 404) : status;

					// for webkit, special in android
					if (status === 0 && response) {
						log.warn('ajax strange status: 0');
						status = 200;
					}

					// normalize IE bug (http://bugs.jquery.com/ticket/1450)
					status = status === 1223 ? 204 : status;

					callback(status, response, headersString);
					$browser.$$completeOutstandingRequest(noop);

					config.$requestCount--;
					if (config.$requestCount === 0)
						config.afterReceived();

				}

			}

			function antiCacheURL(url) {
				if (!antiCache)
					return url;
				return uniqueURL(url, antiCache === 1 ? vx.$TIMESTAMP : now());
				function uniqueURL(url, ts) {
					// used for antiCache
					var mm = url.split('#');
					var rts = /([?&])_=[^&]*/, rquery = /\?/;
					// try replacing _= if it is there
					var ret = mm[0].replace(rts, "$1_=" + ts);
					// if nothing was replaced, add timestamp to the end
					return ret + ((ret === mm[0] ) ? (rquery.test(mm[0]) ? "&" : "?" ) + "_=" + ts : "" ) + (mm[1] ? '#' + mm[1] : '');
				}

			}

			function scriptReq(url, done, el, type, timeout) {
				// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
				// - fetches local scripts via XHR and evals them
				// - adds and immediately removes script elements from the document
				var script = document.createElement('script'), doneWrapper = function(status) {
					if (msie < 9)
						script.onreadystatechange = noop;
					else
						script.onload = script.onerror = noop;
					if (deferId) {
						$browser.defer.cancel(deferId);
						deferId = null;
					}
					if (!el)
						document.body.removeChild(script);
					if (done)
						done(status);
				}, deferId, status = 0;
				script.type = type || 'text/javascript';
				script.src = url;
				if (msie < 9) {
					script.onreadystatechange = function() {
						if (/loaded|complete/.test(script.readyState)) {
							doneWrapper(status);
						}
					};
				} else {
					script.onload = function() {
						doneWrapper(status);
					};
					script.onerror = function() {
						status = -2;
						doneWrapper(status);
					};
				}
				(el || document.body).appendChild(script);
				
				if (timeout > 0) {
					deferId = $browser.defer(function() {
						status = -1;
						doneWrapper(status);
					}, timeout);
				}				
				//return doneWrapper;
			}

			function ajaxReq(ajaxSetting) {
				var xhr = new XHR();
				xhr.open(ajaxSetting.type, ajaxSetting.url, ajaxSetting.async);
				forEach(ajaxSetting.headers, function(value, key) {
					if (value)
						xhr.setRequestHeader(key, value);
				});

				var status, deferId;
				// In IE6 and 7, this might be called synchronously when xhr.send below is called and the
				// response is in the cache. the promise api will ensure that to the app code the api is
				// always async
				xhr.onreadystatechange = function() {
					if (status || xhr.readyState == 4) {
						xhr.onreadystatechange = noop;
						if (deferId) {
							$browser.defer.cancel(deferId);
							deferId = null;
						}
						if (!status && ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) {
							if (ajaxSetting.complete)
								ajaxSetting.complete(xhr, 'success');
							ajaxSetting.success(xhr.response || xhr.responseText, xhr.status, xhr);
						} else {
							if (ajaxSetting.complete)
								ajaxSetting.complete(xhr, 'error');
							ajaxSetting.error(xhr, status || xhr.status, 'error');
						}
					}
				};

				if (ajaxSetting.xhrFields.withCredentials) {
					xhr.withCredentials = true;
				}
				if (ajaxSetting.xhrFields.responseType) {
					xhr.responseType = ajaxSetting.xhrFields.responseType;
				}

				if (ajaxSetting.beforeSend && ajaxSetting.beforeSend.call(ajaxSetting.context, xhr, ajaxSetting) === false) {
					status = -2;
					xhr.abort();
					xhr.onreadystatechange();
					return false;
				}

				xhr.send(ajaxSetting.data || '');

				if (ajaxSetting.timeout > 0) {
					deferId = $browser.defer(function() {
						status = -1;
						xhr.abort();
						xhr.onreadystatechange();
					}, ajaxSetting.timeout);
				}

				return xhr;
			}

		}

	}

	////////////////////////////////
	// $TimeoutProvider
	///////////////////////////////
	function $TimeoutProvider() {
		this.$get = ['$rootScope', '$browser', '$q', /*'$exceptionHandler',*/'$log',
		function($rootScope, $browser, $$q, /*$exceptionHandler,*/$log) {
			var deferreds = {};

			/**
			 * @vxdoc function
			 * @name vx.$timeout
			 * @requires $browser
			 *
			 * @description
			 * VX's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
			 * block and delegates any exceptions to
			 * {@link vx.$exceptionHandler $exceptionHandler} service.
			 *
			 * The return value of registering a timeout function is a promise, which will be resolved when
			 * the timeout is reached and the timeout function is executed.
			 *
			 * To cancel a timeout request, call `$timeout.cancel(promise)`.
			 *
			 * In tests you can use {@link vMock.$timeout `$timeout.flush()`} to
			 * synchronously flush the queue of deferred functions.
			 *
			 * @param {function()} fn A function, whose execution should be delayed.
			 * @param {number=} [delay=0] Delay in milliseconds.
			 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
			 *   will invoke `fn` within the {@link vx.$rootScope.Scope#$apply $apply} block.
			 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
			 *   promise will be resolved with is the return value of the `fn` function.
			 */
			function timeout(fn, delay, invokeApply) {
				var scope = $rootScope, $q = $$q;
				if (isScope(invokeApply)) {
					scope = invokeApply;
					$q = $$q.$new(scope);
				}
				var deferred = $q.defer(), promise = deferred.promise, skipApply = (isDefined(invokeApply) && !invokeApply), timeoutId, cleanup;
				timeoutId = $browser.defer(function() {
					try {
						deferred.resolve(fn());
					} catch(e) {
						deferred.reject(e);
						$log.error(formatError(e));
						//$exceptionHandler(e);
					}

					if (!skipApply)
						scope.$apply(null, '$timeout');
				}, delay);
				cleanup = function() {
					delete deferreds[promise.$$timeoutId];
				};

				promise.$$timeoutId = timeoutId;
				deferreds[timeoutId] = deferred;
				promise.then(cleanup, cleanup);

				return promise;
			}

			/**
			 * @vxdoc function
			 * @name vx.$timeout#cancel
			 * @methodOf vx.$timeout
			 *
			 * @description
			 * Cancels a task associated with the `promise`. As a result of this, the promise will be
			 * resolved with a rejection.
			 *
			 * @param {Promise=} promise Promise returned by the `$timeout` function.
			 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
			 *   canceled.
			 */
			timeout.cancel = function(promise) {
				if (promise && promise.$$timeoutId in deferreds) {
					deferreds[promise.$$timeoutId].reject('canceled');
					return $browser.defer.cancel(promise.$$timeoutId);
				}
				return false;
			};

			return timeout;
		}];

	}

	/**
	 * validation service
	 */
	function $ValidationProvider() {
		var validators = {}, register = this.register = function(name, factory) {
			if (validators.hasOwnProperty(name))
				throw vError(53, 'validator "{0}" has registerred, cannot override it', name);
			return validators[name] = newValidator(name, factory);
		};

		function newValidator(name, factory) {
			return function() {
				var v;
				if (isFunction(factory))
					v = _fastInvoke(factory, null, arguments);
				else if (isRegExp(factory) || (isString(factory) && factory.indexOf('/') === 0))
					v = patternFactory(factory);
				else {
					v = evaluateFactory(factory);
					v.$parse = true;
				}
				v.type = name;
				return v;
			};
		}


		Factory.$inject = ['$parse'];
		function Factory($parse) {
			get.validators = validators;
			get.use = function(ctrl, fn) {
				var validatorFn = fn, args, type = fn.type;
				if (isString(fn)) {
					args = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
					if (fn.charAt(0) === ':') {
						validatorFn = newValidator(fn.substring(1), args[0]);
					} else {
						validatorFn = get(fn);
					}
					validatorFn = _fastInvoke(validatorFn, null, args);
					type = validatorFn.type;
					if (validatorFn.$parse)
						validatorFn = validatorFn($parse);
				}
				if (isFunction(validatorFn) || isArray(validatorFn)) {
					setupValidation(type, validatorFn, ctrl, ctrl.$parsers, ctrl.$$parsers, null);
					setupValidation(type, validatorFn, ctrl, ctrl.$formatters, ctrl.$$formatters, '');
				} else {
					if (validatorFn.parse)
						setupValidation(type, validatorFn.parse, ctrl, ctrl.$parsers, ctrl.$$parsers, null);
					if (validatorFn.format)
						setupValidation(type, validatorFn.format, ctrl, ctrl.$formatters, ctrl.$$formatters, '');
				}
			};

			function setupValidation(key, validatorFn, ctrl, holder, $holder, emptyValue) {
				var fn = validatorFn;
				if (isArray(validatorFn))
					fn = validatorFn[validatorFn.length - 1];
				var validateFn = function(valueToValidate) {
					var empty = isEmpty(valueToValidate), val;
					if (empty) {// un process validation
						ctrl.$setValidity(key, null);
						return emptyValue;
					} else if (( val = _fastInvoke(fn, null, arguments)) !== undefined) {// valid
						ctrl.$setValidity(key, true);
						return val;
					} else {
						ctrl.$setValidity(key, false);
						return valueToValidate;
					}
				};

				if (!isArray(validatorFn))
					holder.push(validateFn);
				else {
					// need copy
					var copy = [];
					forEach(validatorFn, function(value) {
						copy.push(value);
					});
					copy[validatorFn.length - 1] = validateFn;
					$holder.push(copy);
				}
			}

			function get(name) {
				if (!validators.hasOwnProperty(name))
					throw vError(54, 'no validator: {0}', name);
				return validators[name];
			}

			return get;

		}


		this.$get = Factory;

		function evaluateFactory(exp) {
			return function($parse) {
				if (isString(exp) && exp.match(/^\((.*)\)$/))
					exp = exp.substr(1, exp.length - 2);
				var expression = $parse(exp);
				expression.exp = exp;
				return ['$value', '$scope',
				function($value, $scope) {
					var ret = expression($scope, {
						$value : $value
					});
					return ret ? $value : undefined;
				}];
			};
		}

		function patternFactory(regexp) {
			var pattern = regexp;
			if (isString(regexp) && regexp.match(/^\/(.*)\/$/)) {
				regexp = regexp.substr(1, regexp.length - 2);
				pattern = (new RegExp(regexp));
			}
			return function(value) {
				var exp = pattern, match;
				match = exp.test(value);
				return match ? value : undefined;
			};
		}

		register('evaluate', evaluateFactory);

		// pattern
		register('pattern', patternFactory);

		register('minlength', function(minlength) {
			return function(value) {
				return (value.length >= minlength) ? value : undefined;
			};
		});

		register('maxlength', function(maxlength) {
			return function(value) {
				return (value.length <= maxlength) ? value : undefined;
			};
		});

		var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
		register('number', function() {
			return {
				parse : function(value) {
					if (NUMBER_REGEXP.test(value)) {
						return parseFloat(value);
					} else {
						return undefined;
					}
				},
				format : function(value) {
					return '' + value;
				}
			};
		});

		register('min', function(min) {
			return function(value) {
				return (isNumber(value) && value >= min) ? value : undefined;
			};
		});

		register('max', function(max) {
			return function(value) {
				return (isNumber(value) && value <= max) ? value : undefined;
			};
		});

		// regex alias
		register('url', /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/);
		register('email', /^[A-Za-z0-9\._%+\-]+@[A-Za-z0-9\.\-]+\.[A-Za-z]{2,4}$/);
	}